aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backends/audiocd/audiocd-stream.cpp199
-rw-r--r--backends/audiocd/audiocd-stream.h108
-rw-r--r--backends/audiocd/audiocd.h77
-rw-r--r--backends/audiocd/default/default-audiocd.cpp93
-rw-r--r--backends/audiocd/default/default-audiocd.h45
-rw-r--r--backends/audiocd/linux/linux-audiocd.cpp469
-rw-r--r--backends/audiocd/linux/linux-audiocd.h62
-rw-r--r--backends/audiocd/macosx/macosx-audiocd.cpp306
-rw-r--r--backends/audiocd/macosx/macosx-audiocd.h61
-rw-r--r--backends/audiocd/sdl/sdl-audiocd.cpp63
-rw-r--r--backends/audiocd/sdl/sdl-audiocd.h11
-rw-r--r--backends/audiocd/win32/msvc/ntddcdrm.h362
-rw-r--r--backends/audiocd/win32/win32-audiocd.cpp388
-rw-r--r--backends/audiocd/win32/win32-audiocd.h60
-rw-r--r--backends/module.mk8
-rw-r--r--backends/platform/dc/dc.h21
-rw-r--r--backends/platform/dc/dcmain.cpp60
-rw-r--r--backends/platform/ds/arm9/source/osystem_ds.cpp2
-rw-r--r--backends/platform/ds/arm9/source/osystem_ds.h3
-rw-r--r--backends/platform/sdl/macosx/macosx.cpp7
-rw-r--r--backends/platform/sdl/macosx/macosx.h5
-rw-r--r--backends/platform/sdl/posix/posix.cpp13
-rw-r--r--backends/platform/sdl/posix/posix.h2
-rw-r--r--backends/platform/sdl/sdl.cpp19
-rw-r--r--backends/platform/sdl/sdl.h5
-rw-r--r--backends/platform/sdl/win32/win32.cpp5
-rw-r--r--backends/platform/sdl/win32/win32.h4
-rw-r--r--base/commandLine.cpp5
-rwxr-xr-xconfigure14
-rw-r--r--engines/agos/event.cpp4
-rw-r--r--engines/agos/res_snd.cpp2
-rw-r--r--engines/cine/main_loop.cpp2
-rw-r--r--engines/cine/sound.cpp4
-rw-r--r--engines/drascula/drascula.cpp4
-rw-r--r--engines/drascula/sound.cpp2
-rw-r--r--engines/gob/gob.cpp4
-rw-r--r--engines/groovie/groovie.cpp6
-rw-r--r--engines/kyra/sound_towns.cpp27
-rw-r--r--engines/made/made.cpp6
-rw-r--r--engines/sci/sound/audio.cpp9
-rw-r--r--engines/sci/sound/audio.h1
-rw-r--r--engines/scumm/scumm.cpp5
-rw-r--r--engines/scumm/sound.cpp2
-rw-r--r--engines/teenagent/teenagent.cpp4
-rw-r--r--engines/tinsel/tinsel.cpp6
45 files changed, 2339 insertions, 226 deletions
diff --git a/backends/audiocd/audiocd-stream.cpp b/backends/audiocd/audiocd-stream.cpp
new file mode 100644
index 0000000000..3c0d0957da
--- /dev/null
+++ b/backends/audiocd/audiocd-stream.cpp
@@ -0,0 +1,199 @@
+/* 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.
+ *
+ * Original license header:
+ *
+ * Cabal - Legacy Game Implementations
+ *
+ * Cabal 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 "backends/audiocd/audiocd-stream.h"
+#include "common/textconsole.h"
+
+AudioCDStream::AudioCDStream() : _buffer(), _frame(0), _bufferPos(0), _bufferFrame(0), _forceStop(false) {
+}
+
+AudioCDStream::~AudioCDStream() {
+ // Stop the timer; the subclass needs to do this,
+ // so this is just a last resort.
+ stopTimer();
+
+ // Clear any buffered frames
+ emptyQueue();
+}
+
+int AudioCDStream::readBuffer(int16 *buffer, const int numSamples) {
+ int samples = 0;
+
+ // See if any data is left first
+ while (_bufferPos < kSamplesPerFrame && samples < numSamples)
+ buffer[samples++] = _buffer[_bufferPos++];
+
+ // Bail out if done
+ if (endOfData())
+ return samples;
+
+ while (samples < numSamples && !endOfData()) {
+ if (!readNextFrame())
+ return samples;
+
+ // Copy the samples over
+ for (_bufferPos = 0; _bufferPos < kSamplesPerFrame && samples < numSamples;)
+ buffer[samples++] = _buffer[_bufferPos++];
+ }
+
+ return samples;
+}
+
+bool AudioCDStream::readNextFrame() {
+ // Fetch a frame from the queue
+ int16 *buffer;
+
+ {
+ Common::StackLock lock(_mutex);
+
+ // Nothing we can do if it's empty
+ if (_bufferQueue.empty())
+ return false;
+
+ buffer = _bufferQueue.pop();
+ }
+
+ memcpy(_buffer, buffer, kSamplesPerFrame * 2);
+ delete[] buffer;
+ _frame++;
+ return true;
+}
+
+bool AudioCDStream::endOfData() const {
+ return !shouldForceStop() && getStartFrame() + _frame >= getEndFrame() && _bufferPos == kSamplesPerFrame;
+}
+
+bool AudioCDStream::seek(const Audio::Timestamp &where) {
+ // Stop the timer
+ stopTimer();
+
+ // Clear anything out of the queue
+ emptyQueue();
+
+ // Convert to the frame number
+ // Really not much else needed
+ _bufferPos = kSamplesPerFrame;
+ _frame = where.convertToFramerate(kFramesPerSecond).totalNumberOfFrames();
+ _bufferFrame = _frame;
+
+ // Start the timer again
+ startTimer();
+ return true;
+}
+
+Audio::Timestamp AudioCDStream::getLength() const {
+ return Audio::Timestamp(0, getEndFrame() - getStartFrame(), kFramesPerSecond);
+}
+
+void AudioCDStream::timerProc(void *refCon) {
+ static_cast<AudioCDStream *>(refCon)->onTimer();
+}
+
+void AudioCDStream::onTimer() {
+ // The goal here is to do as much work in this timer instead
+ // of doing it in the readBuffer() call, which is the mixer.
+
+ // If we're done, bail.
+ if (shouldForceStop() || getStartFrame() + _bufferFrame >= getEndFrame())
+ return;
+
+ // Get a quick count of the number of items in the queue
+ // We don't care that much; we only need a quick estimate
+ _mutex.lock();
+ uint32 queueCount = _bufferQueue.size();
+ _mutex.unlock();
+
+ // If we have enough audio buffered, bail out
+ if (queueCount >= kBufferThreshold)
+ return;
+
+ while (!shouldForceStop() && queueCount < kBufferThreshold && getStartFrame() + _bufferFrame < getEndFrame()) {
+ int16 *buffer = new int16[kSamplesPerFrame];
+
+ // Figure out the MSF of the frame we're looking for
+ int frame = _bufferFrame + getStartFrame();
+
+ // Request to read that frame
+ if (!readFrame(frame, buffer)) {
+ warning("Failed to read CD audio");
+ forceStop();
+ return;
+ }
+
+ _bufferFrame++;
+
+ // Now push the buffer onto the queue
+ Common::StackLock lock(_mutex);
+ _bufferQueue.push(buffer);
+ queueCount = _bufferQueue.size();
+ }
+}
+
+void AudioCDStream::startTimer(bool fillBuffer) {
+ _forceStop = false;
+ if (fillBuffer) {
+ onTimer();
+ }
+ g_system->getTimerManager()->installTimerProc(timerProc, 10 * 1000, this, "AudioCDStream");
+}
+
+void AudioCDStream::stopTimer() {
+ forceStop();
+ g_system->getTimerManager()->removeTimerProc(timerProc);
+}
+
+void AudioCDStream::emptyQueue() {
+ while (!_bufferQueue.empty())
+ delete[] _bufferQueue.pop();
+}
+
+bool AudioCDStream::shouldForceStop() const {
+ Common::StackLock lock(_forceStopMutex);
+ return _forceStop;
+}
+
+void AudioCDStream::forceStop() {
+ Common::StackLock lock(_forceStopMutex);
+ _forceStop = true;
+}
diff --git a/backends/audiocd/audiocd-stream.h b/backends/audiocd/audiocd-stream.h
new file mode 100644
index 0000000000..dbc6a6321b
--- /dev/null
+++ b/backends/audiocd/audiocd-stream.h
@@ -0,0 +1,108 @@
+/* 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.
+ *
+ * Original license header:
+ *
+ * Cabal - Legacy Game Implementations
+ *
+ * Cabal 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.
+ *
+ */
+
+#ifndef BACKENDS_AUDIOCD_AUDIOCD_STREAM_H
+#define BACKENDS_AUDIOCD_AUDIOCD_STREAM_H
+
+#include "audio/audiostream.h"
+#include "common/mutex.h"
+#include "common/queue.h"
+#include "common/timer.h"
+
+class AudioCDStream : public Audio::SeekableAudioStream {
+public:
+ AudioCDStream();
+ ~AudioCDStream();
+
+ int readBuffer(int16 *buffer, const int numSamples);
+ bool isStereo() const { return true; }
+ int getRate() const { return 44100; }
+ bool endOfData() const;
+ bool seek(const Audio::Timestamp &where);
+ Audio::Timestamp getLength() const;
+
+protected:
+ virtual uint getStartFrame() const = 0;
+ virtual uint getEndFrame() const = 0;
+ virtual bool readFrame(int frame, int16 *buffer) = 0;
+
+ void startTimer(bool fillBuffer = false);
+ void stopTimer();
+
+ enum {
+ kBytesPerFrame = 2352,
+ kSamplesPerFrame = kBytesPerFrame / 2
+ };
+
+ enum {
+ kSecondsPerMinute = 60,
+ kFramesPerSecond = 75
+ };
+
+ enum {
+ // Keep about a second's worth of audio in the buffer
+ kBufferThreshold = kFramesPerSecond
+ };
+
+private:
+ int16 _buffer[kSamplesPerFrame];
+ int _frame;
+ uint _bufferPos;
+
+ Common::Queue<int16 *> _bufferQueue;
+ int _bufferFrame;
+ Common::Mutex _mutex;
+
+ bool _forceStop;
+ bool shouldForceStop() const;
+ void forceStop();
+ Common::Mutex _forceStopMutex;
+
+ bool readNextFrame();
+ static void timerProc(void *refCon);
+ void onTimer();
+ void emptyQueue();
+};
+
+#endif
diff --git a/backends/audiocd/audiocd.h b/backends/audiocd/audiocd.h
index 6eae8e096b..b3674f2570 100644
--- a/backends/audiocd/audiocd.h
+++ b/backends/audiocd/audiocd.h
@@ -48,26 +48,31 @@ public:
};
/**
- * @name Emulated playback functions
- * Engines should call these functions. Not all platforms
- * support cd playback, and these functions should try to
- * emulate it.
+ * Initialize the specified CD drive for audio playback.
+ * @return true if the CD drive was inited successfully
+ */
+ virtual bool open() = 0;
+
+ /**
+ * Close the currently open CD drive
*/
- //@{
+ virtual void close() = 0;
/**
* Start audio CD playback
- * @param track the track to play.
- * @param numLoops how often playback should be repeated (-1 = infinitely often).
- * @param startFrame the frame at which playback should start (75 frames = 1 second).
- * @param duration the number of frames to play.
- * @param only_emulate determines if the track should be emulated only
+ * @param track the track to play.
+ * @param numLoops how often playback should be repeated (<=0 means infinitely often).
+ * @param startFrame the frame at which playback should start (75 frames = 1 second).
+ * @param duration the number of frames to play.
+ * @param onlyEmulate determines if the track should be emulated only
+ * @note The @c onlyEmulate parameter is deprecated.
+ * @return @c true if the track started playing, @c false otherwise
*/
- virtual void play(int track, int numLoops, int startFrame, int duration, bool only_emulate = false) = 0;
+ virtual bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false) = 0;
/**
* Get if audio is being played.
- * @return true if CD or emulated audio is playing
+ * @return true if CD audio is playing
*/
virtual bool isPlaying() const = 0;
@@ -82,12 +87,12 @@ public:
virtual void setBalance(int8 balance) = 0;
/**
- * Stop CD or emulated audio playback.
+ * Stop audio playback.
*/
virtual void stop() = 0;
/**
- * Update CD or emulated audio status.
+ * Update audio status.
*/
virtual void update() = 0;
@@ -96,50 +101,6 @@ public:
* @return a Status struct with playback data.
*/
virtual Status getStatus() const = 0;
-
- //@}
-
-
- /**
- * @name Real CD audio methods
- * These functions should be called from the emulated
- * ones if they can't emulate the audio playback.
- */
- //@{
-
- /**
- * Initialize the specified CD drive for audio playback.
- * @param drive the drive id
- * @return true if the CD drive was inited successfully
- */
- virtual bool openCD(int drive) = 0;
-
- /**
- * Poll CD status.
- * @return true if CD audio is playing
- */
- virtual bool pollCD() const = 0;
-
- /**
- * Start CD audio playback.
- * @param track the track to play.
- * @param num_loops how often playback should be repeated (-1 = infinitely often).
- * @param start_frame the frame at which playback should start (75 frames = 1 second).
- * @param duration the number of frames to play.
- */
- virtual void playCD(int track, int num_loops, int start_frame, int duration) = 0;
-
- /**
- * Stop CD audio playback.
- */
- virtual void stopCD() = 0;
-
- /**
- * Update CD audio status.
- */
- virtual void updateCD() = 0;
-
- //@}
};
#endif
diff --git a/backends/audiocd/default/default-audiocd.cpp b/backends/audiocd/default/default-audiocd.cpp
index abf80ac4cd..c2ce7cedcc 100644
--- a/backends/audiocd/default/default-audiocd.cpp
+++ b/backends/audiocd/default/default-audiocd.cpp
@@ -22,6 +22,7 @@
#include "backends/audiocd/default/default-audiocd.h"
#include "audio/audiostream.h"
+#include "common/config-manager.h"
#include "common/system.h"
DefaultAudioCDManager::DefaultAudioCDManager() {
@@ -37,7 +38,25 @@ DefaultAudioCDManager::DefaultAudioCDManager() {
assert(_mixer);
}
-void DefaultAudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool only_emulate) {
+DefaultAudioCDManager::~DefaultAudioCDManager() {
+ // Subclasses should call close as well
+ close();
+}
+
+bool DefaultAudioCDManager::open() {
+ // For emulation, opening is always valid
+ close();
+ return true;
+}
+
+void DefaultAudioCDManager::close() {
+ // Only need to stop for emulation
+ stop();
+}
+
+bool DefaultAudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate) {
+ stop();
+
if (numLoops != 0 || startFrame != 0) {
_cd.track = track;
_cd.numLoops = numLoops;
@@ -55,9 +74,6 @@ void DefaultAudioCDManager::play(int track, int numLoops, int startFrame, int du
for (int i = 0; !stream && i < 2; ++i)
stream = Audio::SeekableAudioStream::openStreamFile(trackName[i]);
- // Stop any currently playing emulated track
- _mixer->stopHandle(_handle);
-
if (stream != 0) {
Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75);
Audio::Timestamp end = duration ? Audio::Timestamp(0, startFrame + duration, 75) : stream->getLength();
@@ -70,12 +86,11 @@ void DefaultAudioCDManager::play(int track, int numLoops, int startFrame, int du
_emulating = true;
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_handle,
Audio::makeLoopingAudioStream(stream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops), -1, _cd.volume, _cd.balance);
- } else {
- _emulating = false;
- if (!only_emulate)
- playCD(track, numLoops, startFrame, duration);
+ return true;
}
}
+
+ return false;
}
void DefaultAudioCDManager::stop() {
@@ -83,52 +98,32 @@ void DefaultAudioCDManager::stop() {
// Audio CD emulation
_mixer->stopHandle(_handle);
_emulating = false;
- } else {
- // Real Audio CD
- stopCD();
}
}
bool DefaultAudioCDManager::isPlaying() const {
- if (_emulating) {
- // Audio CD emulation
+ // Audio CD emulation
+ if (_emulating)
return _mixer->isSoundHandleActive(_handle);
- } else {
- // Real Audio CD
- return pollCD();
- }
+
+ // The default class only handles emulation
+ return false;
}
void DefaultAudioCDManager::setVolume(byte volume) {
_cd.volume = volume;
- if (_emulating) {
- // Audio CD emulation
- if (_mixer->isSoundHandleActive(_handle))
- _mixer->setChannelVolume(_handle, _cd.volume);
- } else {
- // Real Audio CD
- // Unfortunately I can't implement this atm
- // since SDL doesn't seem to offer an interface method for this.
-
- // g_system->setVolumeCD(_cd.volume);
- }
+ // Audio CD emulation
+ if (_emulating && isPlaying())
+ _mixer->setChannelVolume(_handle, _cd.volume);
}
void DefaultAudioCDManager::setBalance(int8 balance) {
_cd.balance = balance;
- if (_emulating) {
- // Audio CD emulation
- if (isPlaying())
- _mixer->setChannelBalance(_handle, _cd.balance);
- } else {
- // Real Audio CD
- // Unfortunately I can't implement this atm
- // since SDL doesn't seem to offer an interface method for this.
-
- // g_system->setBalanceCD(_cd.balance);
- }
+ // Audio CD emulation
+ if (_emulating && isPlaying())
+ _mixer->setChannelBalance(_handle, _cd.balance);
}
void DefaultAudioCDManager::update() {
@@ -142,8 +137,6 @@ void DefaultAudioCDManager::update() {
// or not.
_emulating = false;
}
- } else {
- updateCD();
}
}
@@ -152,3 +145,21 @@ DefaultAudioCDManager::Status DefaultAudioCDManager::getStatus() const {
info.playing = isPlaying();
return info;
}
+
+bool DefaultAudioCDManager::openRealCD() {
+ Common::String cdrom = ConfMan.get("cdrom");
+
+ // Try to parse it as an int
+ char *endPos;
+ int drive = strtol(cdrom.c_str(), &endPos, 0);
+
+ // If not an integer, treat as a drive path
+ if (endPos == cdrom.c_str())
+ return openCD(cdrom);
+
+ if (drive < 0)
+ return false;
+
+ return openCD(drive);
+}
+
diff --git a/backends/audiocd/default/default-audiocd.h b/backends/audiocd/default/default-audiocd.h
index 9e4ba6b33e..e3fbb4b5a1 100644
--- a/backends/audiocd/default/default-audiocd.h
+++ b/backends/audiocd/default/default-audiocd.h
@@ -26,29 +26,48 @@
#include "backends/audiocd/audiocd.h"
#include "audio/mixer.h"
+namespace Common {
+class String;
+} // End of namespace Common
+
/**
* The default audio cd manager. Implements emulation of audio cd playback.
*/
class DefaultAudioCDManager : public AudioCDManager {
public:
DefaultAudioCDManager();
- virtual ~DefaultAudioCDManager() {}
-
- void play(int track, int numLoops, int startFrame, int duration, bool only_emulate = false);
- void stop();
- bool isPlaying() const;
- void setVolume(byte volume);
- void setBalance(int8 balance);
- void update();
+ virtual ~DefaultAudioCDManager();
+
+ virtual bool open();
+ virtual void close();
+ virtual bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false);
+ virtual void stop();
+ virtual bool isPlaying() const;
+ virtual void setVolume(byte volume);
+ virtual void setBalance(int8 balance);
+ virtual void update();
virtual Status getStatus() const; // Subclasses should override for better status results
+protected:
+ /**
+ * Open a CD using the cdrom config variable
+ */
+ bool openRealCD();
+
+ /**
+ * Open a CD using the specified drive index
+ * @param drive The index of the drive
+ * @note The index is implementation-defined, but 0 is always the best choice
+ */
virtual bool openCD(int drive) { return false; }
- virtual void updateCD() {}
- virtual bool pollCD() const { return false; }
- virtual void playCD(int track, int num_loops, int start_frame, int duration) {}
- virtual void stopCD() {}
-protected:
+ /**
+ * Open a CD from a specific drive
+ * @param drive The name of the drive/path
+ * @note The drive parameter is platform-specific
+ */
+ virtual bool openCD(const Common::String &drive) { return false; }
+
Audio::SoundHandle _handle;
bool _emulating;
diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp
new file mode 100644
index 0000000000..a5838e1c5a
--- /dev/null
+++ b/backends/audiocd/linux/linux-audiocd.cpp
@@ -0,0 +1,469 @@
+/* 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.
+ *
+ * Original license header:
+ *
+ * Cabal - Legacy Game Implementations
+ *
+ * Cabal 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.
+ *
+ */
+
+// Enable all forbidden symbols to allow us to include and use necessary APIs.
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/audiocd/linux/linux-audiocd.h"
+
+#ifdef USE_LINUXCD
+
+#include "backends/audiocd/audiocd-stream.h"
+#include "backends/audiocd/default/default-audiocd.h"
+#include "common/array.h"
+#include "common/config-manager.h"
+#include "common/str.h"
+#include "common/debug.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/cdrom.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+enum {
+ kLeadoutTrack = 0xAA
+};
+
+enum {
+ kBytesPerFrame = 2352,
+ kSamplesPerFrame = kBytesPerFrame / 2
+};
+
+enum {
+ kSecondsPerMinute = 60,
+ kFramesPerSecond = 75
+};
+
+enum {
+ // Keep about a second's worth of audio in the buffer
+ kBufferThreshold = kFramesPerSecond
+};
+
+static int getFrameCount(const cdrom_msf0 &msf) {
+ int time = msf.minute;
+ time *= kSecondsPerMinute;
+ time += msf.second;
+ time *= kFramesPerSecond;
+ time += msf.frame;
+ return time;
+}
+
+// Helper function to convert an error code into a human-readable message
+static Common::String getErrorMessage(int errorCode) {
+ char buf[256];
+ buf[0] = 0;
+
+#ifdef _GNU_SOURCE
+ // glibc sucks
+ return Common::String(strerror_r(errorCode, buf, sizeof(buf)));
+#else
+ strerror_r(errorCode, buf, sizeof(buf));
+ return Common::String(buf);
+#endif
+}
+
+class LinuxAudioCDStream : public AudioCDStream {
+public:
+ LinuxAudioCDStream(int fd, const cdrom_tocentry &startEntry, const cdrom_tocentry &endEntry);
+ ~LinuxAudioCDStream();
+
+protected:
+ uint getStartFrame() const;
+ uint getEndFrame() const;
+ bool readFrame(int frame, int16 *buffer);
+
+private:
+ int _fd;
+ const cdrom_tocentry &_startEntry, &_endEntry;
+};
+
+LinuxAudioCDStream::LinuxAudioCDStream(int fd, const cdrom_tocentry &startEntry, const cdrom_tocentry &endEntry) :
+ _fd(fd), _startEntry(startEntry), _endEntry(endEntry) {
+ // We fill the buffer here already to prevent any out of sync issues due
+ // to the CD not yet having spun up.
+ startTimer(true);
+}
+
+LinuxAudioCDStream::~LinuxAudioCDStream() {
+ stopTimer();
+}
+
+bool LinuxAudioCDStream::readFrame(int frame, int16 *buffer) {
+ // Create the argument
+ union {
+ cdrom_msf msf;
+ char buffer[kBytesPerFrame];
+ } arg;
+
+ int seconds = frame / kFramesPerSecond;
+ frame %= kFramesPerSecond;
+ int minutes = seconds / kSecondsPerMinute;
+ seconds %= kSecondsPerMinute;
+
+ // Request to read that frame
+ // We don't use CDROMREADAUDIO, as it seems to cause kernel
+ // panics on ejecting discs. Probably bad to eject the disc
+ // while playing, but at least let's try to prevent that case.
+ arg.msf.cdmsf_min0 = minutes;
+ arg.msf.cdmsf_sec0 = seconds;
+ arg.msf.cdmsf_frame0 = frame;
+ // The "end" part is irrelevant (why isn't cdrom_msf0 the type
+ // instead?)
+
+ if (ioctl(_fd, CDROMREADRAW, &arg) < 0) {
+ warning("Failed to CD read audio: %s", getErrorMessage(errno).c_str());
+ return false;
+ }
+
+ memcpy(buffer, arg.buffer, kBytesPerFrame);
+ return true;
+}
+
+uint LinuxAudioCDStream::getStartFrame() const {
+ return getFrameCount(_startEntry.cdte_addr.msf);
+}
+
+uint LinuxAudioCDStream::getEndFrame() const {
+ return getFrameCount(_endEntry.cdte_addr.msf);
+}
+
+
+class LinuxAudioCDManager : public DefaultAudioCDManager {
+public:
+ LinuxAudioCDManager();
+ ~LinuxAudioCDManager();
+
+ bool open();
+ void close();
+ bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false);
+
+protected:
+ bool openCD(int drive);
+ bool openCD(const Common::String &drive);
+
+private:
+ struct Device {
+ Device(const Common::String &n, dev_t d) : name(n), device(d) {}
+ Common::String name;
+ dev_t device;
+ };
+
+ typedef Common::Array<Device> DeviceList;
+ DeviceList scanDevices();
+ bool tryAddDrive(DeviceList &devices, const Common::String &drive);
+ bool tryAddDrive(DeviceList &devices, const Common::String &drive, dev_t device);
+ bool tryAddDrive(DeviceList &devices, dev_t device);
+ bool tryAddPath(DeviceList &devices, const Common::String &path);
+ bool tryAddGamePath(DeviceList &devices);
+ bool loadTOC();
+ static bool hasDevice(const DeviceList &devices, dev_t device);
+
+ int _fd;
+ cdrom_tochdr _tocHeader;
+ Common::Array<cdrom_tocentry> _tocEntries;
+};
+
+static bool isTrayEmpty(int errorNumber) {
+ switch (errorNumber) {
+ case EIO:
+ case ENOENT:
+ case EINVAL:
+#ifdef ENOMEDIUM
+ case ENOMEDIUM:
+#endif
+ return true;
+ }
+
+ return false;
+}
+
+LinuxAudioCDManager::LinuxAudioCDManager() {
+ _fd = -1;
+ memset(&_tocHeader, 0, sizeof(_tocHeader));
+}
+
+LinuxAudioCDManager::~LinuxAudioCDManager() {
+ close();
+}
+
+bool LinuxAudioCDManager::open() {
+ close();
+
+ if (openRealCD())
+ return true;
+
+ return DefaultAudioCDManager::open();
+}
+
+void LinuxAudioCDManager::close() {
+ DefaultAudioCDManager::close();
+
+ if (_fd < 0)
+ return;
+
+ ::close(_fd);
+ memset(&_tocHeader, 0, sizeof(_tocHeader));
+ _tocEntries.clear();
+}
+
+bool LinuxAudioCDManager::openCD(int drive) {
+ DeviceList devices = scanDevices();
+ if (drive >= (int)devices.size())
+ return false;
+
+ _fd = ::open(devices[drive].name.c_str(), O_RDONLY | O_NONBLOCK, 0);
+ if (_fd < 0)
+ return false;
+
+ if (!loadTOC()) {
+ close();
+ return false;
+ }
+
+ return true;
+}
+
+bool LinuxAudioCDManager::openCD(const Common::String &drive) {
+ DeviceList devices;
+ if (!tryAddDrive(devices, drive) && !tryAddPath(devices, drive))
+ return false;
+
+ _fd = ::open(devices[0].name.c_str(), O_RDONLY | O_NONBLOCK, 0);
+ if (_fd < 0)
+ return false;
+
+ if (!loadTOC()) {
+ close();
+ return false;
+ }
+
+ return true;
+}
+
+bool LinuxAudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate) {
+ // Prefer emulation
+ if (DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate))
+ return true;
+
+ // If we're set to only emulate, or have no CD drive, return here
+ if (onlyEmulate || _fd < 0)
+ return false;
+
+ // HACK: For now, just assume that track number is right
+ // That only works because ScummVM uses the wrong track number anyway
+
+ if (track >= (int)_tocEntries.size() - 1) {
+ warning("No such track %d", track);
+ return false;
+ }
+
+ // Bail if the track isn't an audio track
+ if ((_tocEntries[track].cdte_ctrl & 0x04) != 0) {
+ warning("Track %d is not audio", track);
+ return false;
+ }
+
+ // Create the AudioStream and play it
+ debug(1, "Playing CD track %d", track);
+
+ Audio::SeekableAudioStream *audioStream = new LinuxAudioCDStream(_fd, _tocEntries[track], _tocEntries[track + 1]);
+
+ Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75);
+ Audio::Timestamp end = (duration == 0) ? audioStream->getLength() : Audio::Timestamp(0, startFrame + duration, 75);
+
+ // Fake emulation since we're really playing an AudioStream
+ _emulating = true;
+
+ _mixer->playStream(
+ Audio::Mixer::kMusicSoundType,
+ &_handle,
+ Audio::makeLoopingAudioStream(audioStream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops),
+ -1,
+ _cd.volume,
+ _cd.balance,
+ DisposeAfterUse::YES,
+ true);
+
+ return true;
+}
+
+LinuxAudioCDManager::DeviceList LinuxAudioCDManager::scanDevices() {
+ DeviceList devices;
+
+ // Try to use the game's path first as the device
+ tryAddGamePath(devices);
+
+ // Try adding the default CD-ROM
+ tryAddDrive(devices, "/dev/cdrom");
+
+ // TODO: Try others?
+
+ return devices;
+}
+
+bool LinuxAudioCDManager::tryAddDrive(DeviceList &devices, const Common::String &drive) {
+ struct stat stbuf;
+ if (stat(drive.c_str(), &stbuf) < 0)
+ return false;
+
+ // Must be a character or block device
+ if (!S_ISCHR(stbuf.st_mode) && !S_ISBLK(stbuf.st_mode))
+ return false;
+
+ return tryAddDrive(devices, drive, stbuf.st_rdev);
+}
+
+bool LinuxAudioCDManager::tryAddDrive(DeviceList &devices, const Common::String &drive, dev_t device) {
+ if (hasDevice(devices, device))
+ return true;
+
+ // Try opening the device and seeing if it is a CD-ROM drve
+ int fd = ::open(drive.c_str(), O_RDONLY | O_NONBLOCK, 0);
+ if (fd >= 0) {
+ cdrom_subchnl info;
+ info.cdsc_format = CDROM_MSF;
+
+ bool isCD = ioctl(fd, CDROMSUBCHNL, &info) == 0 || isTrayEmpty(errno);
+ ::close(fd);
+ if (isCD) {
+ devices.push_back(Device(drive, device));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool LinuxAudioCDManager::tryAddDrive(DeviceList &devices, dev_t device) {
+ // Construct the block name
+ // (Does anyone have a better way to do this? bdevname is kernel only)
+ Common::String name = Common::String::format("/dev/block/%d:%d", gnu_dev_major(device), gnu_dev_minor(device));
+
+ return tryAddDrive(devices, name, device);
+}
+
+bool LinuxAudioCDManager::tryAddPath(DeviceList &devices, const Common::String &path) {
+ struct stat stbuf;
+ if (stat(path.c_str(), &stbuf) < 0)
+ return false;
+
+ return tryAddDrive(devices, stbuf.st_dev);
+}
+
+bool LinuxAudioCDManager::tryAddGamePath(DeviceList &devices) {
+ if (!ConfMan.hasKey("path"))
+ return false;
+
+ return tryAddPath(devices, ConfMan.get("path"));
+}
+
+bool LinuxAudioCDManager::loadTOC() {
+ if (_fd < 0)
+ return false;
+
+ if (ioctl(_fd, CDROMREADTOCHDR, &_tocHeader) < 0)
+ return false;
+
+ debug(4, "CD: Start Track: %d, End Track %d", _tocHeader.cdth_trk0, _tocHeader.cdth_trk1);
+
+ for (int i = _tocHeader.cdth_trk0; i <= _tocHeader.cdth_trk1; i++) {
+ cdrom_tocentry entry;
+ memset(&entry, 0, sizeof(entry));
+ entry.cdte_track = i;
+ entry.cdte_format = CDROM_MSF;
+
+ if (ioctl(_fd, CDROMREADTOCENTRY, &entry) < 0)
+ return false;
+
+#if 0
+ debug("Entry:");
+ debug("\tTrack: %d", entry.cdte_track);
+ debug("\tAdr: %d", entry.cdte_adr);
+ debug("\tCtrl: %d", entry.cdte_ctrl);
+ debug("\tFormat: %d", entry.cdte_format);
+ debug("\tMSF: %d:%d:%d", entry.cdte_addr.msf.minute, entry.cdte_addr.msf.second, entry.cdte_addr.msf.frame);
+ debug("\tMode: %d\n", entry.cdte_datamode);
+#endif
+
+ _tocEntries.push_back(entry);
+ }
+
+ // Fetch the leadout so we can get the length of the last frame
+ cdrom_tocentry entry;
+ memset(&entry, 0, sizeof(entry));
+ entry.cdte_track = kLeadoutTrack;
+ entry.cdte_format = CDROM_MSF;
+
+ if (ioctl(_fd, CDROMREADTOCENTRY, &entry) < 0)
+ return false;
+
+#if 0
+ debug("Lead out:");
+ debug("\tTrack: %d", entry.cdte_track);
+ debug("\tAdr: %d", entry.cdte_adr);
+ debug("\tCtrl: %d", entry.cdte_ctrl);
+ debug("\tFormat: %d", entry.cdte_format);
+ debug("\tMSF: %d:%d:%d", entry.cdte_addr.msf.minute, entry.cdte_addr.msf.second, entry.cdte_addr.msf.frame);
+ debug("\tMode: %d\n", entry.cdte_datamode);
+#endif
+
+ _tocEntries.push_back(entry);
+ return true;
+}
+
+bool LinuxAudioCDManager::hasDevice(const DeviceList &devices, dev_t device) {
+ for (DeviceList::const_iterator it = devices.begin(); it != devices.end(); it++)
+ if (it->device == device)
+ return true;
+
+ return false;
+}
+
+AudioCDManager *createLinuxAudioCDManager() {
+ return new LinuxAudioCDManager();
+}
+
+#endif // USE_LINUXCD
diff --git a/backends/audiocd/linux/linux-audiocd.h b/backends/audiocd/linux/linux-audiocd.h
new file mode 100644
index 0000000000..09d6353991
--- /dev/null
+++ b/backends/audiocd/linux/linux-audiocd.h
@@ -0,0 +1,62 @@
+/* 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.
+ *
+ * Original license header:
+ *
+ * Cabal - Legacy Game Implementations
+ *
+ * Cabal 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.
+ *
+ */
+
+#ifndef BACKENDS_AUDIOCD_LINUX_H
+#define BACKENDS_AUDIOCD_LINUX_H
+
+#include "common/scummsys.h"
+
+#ifdef USE_LINUXCD
+
+class AudioCDManager;
+
+/**
+ * Create an audio CD manager using the Linux CDROM API
+ */
+AudioCDManager *createLinuxAudioCDManager();
+
+#endif
+
+#endif
+
diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp
new file mode 100644
index 0000000000..e8d41c3e10
--- /dev/null
+++ b/backends/audiocd/macosx/macosx-audiocd.cpp
@@ -0,0 +1,306 @@
+/* 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.
+ *
+ * Original license header:
+ *
+ * Cabal - Legacy Game Implementations
+ *
+ * Cabal 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.
+ *
+ */
+
+#ifdef MACOSX
+
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <limits.h>
+
+#include "common/scummsys.h"
+
+#include "audio/audiostream.h"
+#include "audio/decoders/aiff.h"
+#include "audio/timestamp.h"
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/fs.h"
+#include "common/hashmap.h"
+#include "common/textconsole.h"
+#include "backends/audiocd/default/default-audiocd.h"
+#include "backends/audiocd/macosx/macosx-audiocd.h"
+#include "backends/fs/stdiostream.h"
+
+// Partially based on SDL's code
+
+/**
+ * The Mac OS X audio cd manager. Implements real audio cd playback.
+ */
+class MacOSXAudioCDManager : public DefaultAudioCDManager {
+public:
+ MacOSXAudioCDManager() {}
+ ~MacOSXAudioCDManager();
+
+ bool open();
+ void close();
+ bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false);
+
+protected:
+ bool openCD(int drive);
+ bool openCD(const Common::String &drive);
+
+private:
+ struct Drive {
+ Drive(const Common::String &m, const Common::String &d, const Common::String &f) :
+ mountPoint(m), deviceName(d), fsType(f) {}
+
+ Common::String mountPoint;
+ Common::String deviceName;
+ Common::String fsType;
+ };
+
+ typedef Common::Array<Drive> DriveList;
+ DriveList detectAllDrives();
+ DriveList detectCDDADrives();
+
+ bool findTrackNames(const Common::String &drivePath);
+
+ Common::HashMap<uint, Common::String> _trackMap;
+};
+
+MacOSXAudioCDManager::~MacOSXAudioCDManager() {
+ close();
+}
+
+bool MacOSXAudioCDManager::open() {
+ close();
+
+ if (openRealCD())
+ return true;
+
+ return DefaultAudioCDManager::open();
+}
+
+/**
+ * Find the base disk number of device name.
+ * Returns -1 if mount point is not /dev/disk*
+ */
+static int findBaseDiskNumber(const Common::String &diskName) {
+ if (!diskName.hasPrefix("/dev/disk"))
+ return -1;
+
+ const char *startPtr = diskName.c_str() + 9;
+ char *endPtr;
+ int baseDiskNumber = strtol(startPtr, &endPtr, 10);
+ if (startPtr == endPtr)
+ return -1;
+
+ return baseDiskNumber;
+}
+
+bool MacOSXAudioCDManager::openCD(int drive) {
+ DriveList allDrives = detectAllDrives();
+ if (allDrives.empty())
+ return false;
+
+ DriveList cddaDrives;
+
+ // Try to get the volume related to the game's path
+ if (ConfMan.hasKey("path")) {
+ Common::String gamePath = ConfMan.get("path");
+ struct statfs gamePathStat;
+ if (statfs(gamePath.c_str(), &gamePathStat) == 0) {
+ int baseDiskNumber = findBaseDiskNumber(gamePathStat.f_mntfromname);
+ if (baseDiskNumber >= 0) {
+ // Look for a CDDA drive with the same base disk number
+ for (uint32 i = 0; i < allDrives.size(); i++) {
+ if (allDrives[i].fsType == "cddafs" && findBaseDiskNumber(allDrives[i].deviceName) == baseDiskNumber) {
+ debug(1, "Preferring drive '%s'", allDrives[i].mountPoint.c_str());
+ cddaDrives.push_back(allDrives[i]);
+ allDrives.remove_at(i);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Add the remaining CDDA drives to the CDDA list
+ for (uint32 i = 0; i < allDrives.size(); i++)
+ if (allDrives[i].fsType == "cddafs")
+ cddaDrives.push_back(allDrives[i]);
+
+ if (drive >= (int)cddaDrives.size())
+ return false;
+
+ debug(1, "Using '%s' as the CD drive", cddaDrives[drive].mountPoint.c_str());
+
+ return findTrackNames(cddaDrives[drive].mountPoint);
+}
+
+bool MacOSXAudioCDManager::openCD(const Common::String &drive) {
+ DriveList drives = detectAllDrives();
+
+ for (uint32 i = 0; i < drives.size(); i++) {
+ if (drives[i].fsType != "cddafs")
+ continue;
+
+ if (drives[i].mountPoint == drive || drives[i].deviceName == drive) {
+ debug(1, "Using '%s' as the CD drive", drives[i].mountPoint.c_str());
+ return findTrackNames(drives[i].mountPoint);
+ }
+ }
+
+ return false;
+}
+
+void MacOSXAudioCDManager::close() {
+ DefaultAudioCDManager::close();
+ _trackMap.clear();
+}
+
+enum {
+ // Some crazy high number that we'll never actually hit
+ kMaxDriveCount = 256
+};
+
+MacOSXAudioCDManager::DriveList MacOSXAudioCDManager::detectAllDrives() {
+ // Fetch the lists of drives
+ struct statfs driveStats[kMaxDriveCount];
+ int foundDrives = getfsstat(driveStats, sizeof(driveStats), MNT_WAIT);
+ if (foundDrives <= 0)
+ return DriveList();
+
+ DriveList drives;
+ for (int i = 0; i < foundDrives; i++)
+ drives.push_back(Drive(driveStats[i].f_mntonname, driveStats[i].f_mntfromname, driveStats[i].f_fstypename));
+
+ return drives;
+}
+
+bool MacOSXAudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate) {
+ // Prefer emulation
+ if (DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate))
+ return true;
+
+ // If we're set to only emulate, or have no CD drive, return here
+ if (onlyEmulate || !_trackMap.contains(track))
+ return false;
+
+ if (!numLoops && !startFrame)
+ return false;
+
+ // Now load the AIFF track from the name
+ Common::String fileName = _trackMap[track];
+ Common::SeekableReadStream *stream = StdioStream::makeFromPath(fileName.c_str(), false);
+
+ if (!stream) {
+ warning("Failed to open track '%s'", fileName.c_str());
+ return false;
+ }
+
+ Audio::AudioStream *audioStream = Audio::makeAIFFStream(stream, DisposeAfterUse::YES);
+ if (!audioStream) {
+ warning("Track '%s' is not an AIFF track", fileName.c_str());
+ return false;
+ }
+
+ Audio::SeekableAudioStream *seekStream = dynamic_cast<Audio::SeekableAudioStream *>(audioStream);
+ if (!seekStream) {
+ warning("Track '%s' is not seekable", fileName.c_str());
+ return false;
+ }
+
+ Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75);
+ Audio::Timestamp end = duration ? Audio::Timestamp(0, startFrame + duration, 75) : seekStream->getLength();
+
+ // Fake emulation since we're really playing an AIFF file
+ _emulating = true;
+
+ _mixer->playStream(Audio::Mixer::kMusicSoundType, &_handle,
+ Audio::makeLoopingAudioStream(seekStream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops), -1, _cd.volume, _cd.balance);
+ return true;
+}
+
+bool MacOSXAudioCDManager::findTrackNames(const Common::String &drivePath) {
+ Common::FSNode directory(drivePath);
+
+ if (!directory.exists()) {
+ warning("Directory '%s' does not exist", drivePath.c_str());
+ return false;
+ }
+
+ if (!directory.isDirectory()) {
+ warning("'%s' is not a directory", drivePath.c_str());
+ return false;
+ }
+
+ Common::FSList children;
+ if (!directory.getChildren(children, Common::FSNode::kListFilesOnly)) {
+ warning("Failed to find children for '%s'", drivePath.c_str());
+ return false;
+ }
+
+ for (uint32 i = 0; i < children.size(); i++) {
+ if (!children[i].isDirectory()) {
+ Common::String fileName = children[i].getName();
+
+ if (fileName.hasSuffix(".aiff") || fileName.hasSuffix(".cdda")) {
+ uint j = 0;
+
+ // Search for the track ID in the file name.
+ for (; j < fileName.size() && !Common::isDigit(fileName[j]); j++)
+ ;
+
+ const char *trackIDString = fileName.c_str() + j;
+ char *endPtr = nullptr;
+ long trackID = strtol(trackIDString, &endPtr, 10);
+
+ if (trackIDString != endPtr && trackID > 0 && trackID < UINT_MAX) {
+ _trackMap[trackID - 1] = drivePath + '/' + fileName;
+ } else {
+ warning("Invalid track file name: '%s'", fileName.c_str());
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+AudioCDManager *createMacOSXAudioCDManager() {
+ return new MacOSXAudioCDManager();
+}
+
+#endif // MACOSX
diff --git a/backends/audiocd/macosx/macosx-audiocd.h b/backends/audiocd/macosx/macosx-audiocd.h
new file mode 100644
index 0000000000..55b8c7b8c6
--- /dev/null
+++ b/backends/audiocd/macosx/macosx-audiocd.h
@@ -0,0 +1,61 @@
+/* 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.
+ *
+ * Original license header:
+ *
+ * Cabal - Legacy Game Implementations
+ *
+ * Cabal 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.
+ *
+ */
+
+#ifndef BACKENDS_AUDIOCD_MACOSX_H
+#define BACKENDS_AUDIOCD_MACOSX_H
+
+#include "common/scummsys.h"
+
+#ifdef MACOSX
+
+class AudioCDManager;
+
+/**
+ * Create an audio CD manager for Mac OS X
+ */
+AudioCDManager *createMacOSXAudioCDManager();
+
+#endif
+
+#endif //
diff --git a/backends/audiocd/sdl/sdl-audiocd.cpp b/backends/audiocd/sdl/sdl-audiocd.cpp
index ff50c56af3..3558fb5671 100644
--- a/backends/audiocd/sdl/sdl-audiocd.cpp
+++ b/backends/audiocd/sdl/sdl-audiocd.cpp
@@ -43,10 +43,16 @@ SdlAudioCDManager::SdlAudioCDManager()
}
SdlAudioCDManager::~SdlAudioCDManager() {
- if (_cdrom) {
- SDL_CDStop(_cdrom);
- SDL_CDClose(_cdrom);
- }
+ close();
+}
+
+bool SdlAudioCDManager::open() {
+ close();
+
+ if (openRealCD())
+ return true;
+
+ return DefaultAudioCDManager::open();
}
bool SdlAudioCDManager::openCD(int drive) {
@@ -67,44 +73,69 @@ bool SdlAudioCDManager::openCD(int drive) {
return (_cdrom != NULL);
}
-void SdlAudioCDManager::stopCD() {
+void SdlAudioCDManager::close() {
+ DefaultAudioCDManager::close();
+
+ if (_cdrom) {
+ SDL_CDStop(_cdrom);
+ SDL_CDClose(_cdrom);
+ _cdrom = 0;
+ }
+}
+
+void SdlAudioCDManager::stop() {
+ DefaultAudioCDManager::stop();
+
// Stop CD Audio in 1/10th of a second
_cdStopTime = SDL_GetTicks() + 100;
_cdNumLoops = 0;
}
-void SdlAudioCDManager::playCD(int track, int num_loops, int start_frame, int duration) {
- if (!num_loops && !start_frame)
- return;
+bool SdlAudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate) {
+ // Prefer emulation
+ if (DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate))
+ return true;
- if (!_cdrom)
- return;
+ // If we're set to only emulate, or have no CD, return here
+ if (onlyEmulate || !_cdrom)
+ return false;
+ if (!numLoops && !startFrame)
+ return false;
+
+ // FIXME: Explain this.
if (duration > 0)
duration += 5;
_cdTrack = track;
- _cdNumLoops = num_loops;
- _cdStartFrame = start_frame;
+ _cdNumLoops = numLoops;
+ _cdStartFrame = startFrame;
SDL_CDStatus(_cdrom);
- if (start_frame == 0 && duration == 0)
+ if (startFrame == 0 && duration == 0)
SDL_CDPlayTracks(_cdrom, track, 0, 1, 0);
else
- SDL_CDPlayTracks(_cdrom, track, start_frame, 0, duration);
+ SDL_CDPlayTracks(_cdrom, track, startFrame, 0, duration);
_cdDuration = duration;
_cdStopTime = 0;
_cdEndTime = SDL_GetTicks() + _cdrom->track[track].length * 1000 / CD_FPS;
+
+ return true;
}
-bool SdlAudioCDManager::pollCD() const {
+bool SdlAudioCDManager::isPlaying() const {
+ if (DefaultAudioCDManager::isPlaying())
+ return true;
+
if (!_cdrom)
return false;
return (_cdNumLoops != 0 && (SDL_GetTicks() < _cdEndTime || SDL_CDStatus(_cdrom) == CD_PLAYING));
}
-void SdlAudioCDManager::updateCD() {
+void SdlAudioCDManager::update() {
+ DefaultAudioCDManager::update();
+
if (!_cdrom)
return;
diff --git a/backends/audiocd/sdl/sdl-audiocd.h b/backends/audiocd/sdl/sdl-audiocd.h
index bfad7b6805..91895dac99 100644
--- a/backends/audiocd/sdl/sdl-audiocd.h
+++ b/backends/audiocd/sdl/sdl-audiocd.h
@@ -37,12 +37,15 @@ public:
SdlAudioCDManager();
virtual ~SdlAudioCDManager();
+ virtual bool open();
+ virtual void close();
+ virtual bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false);
+ virtual void stop();
+ virtual bool isPlaying() const;
+ virtual void update();
+
protected:
virtual bool openCD(int drive);
- virtual void updateCD();
- virtual bool pollCD() const;
- virtual void playCD(int track, int num_loops, int start_frame, int duration);
- virtual void stopCD();
SDL_CD *_cdrom;
int _cdTrack, _cdNumLoops, _cdStartFrame, _cdDuration;
diff --git a/backends/audiocd/win32/msvc/ntddcdrm.h b/backends/audiocd/win32/msvc/ntddcdrm.h
new file mode 100644
index 0000000000..18527e2675
--- /dev/null
+++ b/backends/audiocd/win32/msvc/ntddcdrm.h
@@ -0,0 +1,362 @@
+/**
+ * @file ntddcdrm.h
+ * Copyright 2012, 2013 MinGW.org project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+/* Created by Casper S. Hornstrup <chorns@users.sourceforge.net> */
+#ifndef __NTDDCDRM_H
+#define __NTDDCDRM_H
+#if 0 // Added to make MSVC happy.
+#pragma GCC system_header
+#include <_mingw.h>
+#endif
+
+/*
+ * CDROM IOCTL interface.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if 0 // Added to make MSVC happy.
+#include "ntddk.h"
+#include "ntddstor.h"
+#endif
+
+#define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM
+
+#define IOCTL_CDROM_CHECK_VERIFY \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0200, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_FIND_NEW_DEVICES \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0206, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_GET_CONTROL \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x000D, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_GET_DRIVE_GEOMETRY \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0013, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_GET_LAST_SESSION \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x000E, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_GET_VOLUME \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0005, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_PAUSE_AUDIO \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_PLAY_AUDIO_MSF \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0006, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_RAW_READ \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x000F, METHOD_OUT_DIRECT, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_READ_Q_CHANNEL \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x000B, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_READ_TOC \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_READ_TOC_EX \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0015, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_RESUME_AUDIO \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_SEEK_AUDIO_MSF \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_SET_VOLUME \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x000A, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_SIMBAD \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x1003, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_STOP_AUDIO \
+ CTL_CODE(IOCTL_CDROM_BASE, 0x0002, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+
+#define MAXIMUM_NUMBER_TRACKS 100
+#define MAXIMUM_CDROM_SIZE 804
+#define MINIMUM_CDROM_READ_TOC_EX_SIZE 2
+
+typedef struct _TRACK_DATA {
+ UCHAR Reserved;
+ UCHAR Control : 4;
+ UCHAR Adr : 4;
+ UCHAR TrackNumber;
+ UCHAR Reserved1;
+ UCHAR Address[4];
+} TRACK_DATA, *PTRACK_DATA;
+
+/* CDROM_DISK_DATA.DiskData flags */
+#define CDROM_DISK_AUDIO_TRACK 0x00000001
+#define CDROM_DISK_DATA_TRACK 0x00000002
+
+typedef struct _CDROM_DISK_DATA {
+ ULONG DiskData;
+} CDROM_DISK_DATA, *PCDROM_DISK_DATA;
+
+typedef struct _CDROM_PLAY_AUDIO_MSF {
+ UCHAR StartingM;
+ UCHAR StartingS;
+ UCHAR StartingF;
+ UCHAR EndingM;
+ UCHAR EndingS;
+ UCHAR EndingF;
+} CDROM_PLAY_AUDIO_MSF, *PCDROM_PLAY_AUDIO_MSF;
+
+/* CDROM_READ_TOC_EX.Format constants */
+#define CDROM_READ_TOC_EX_FORMAT_TOC 0x00
+#define CDROM_READ_TOC_EX_FORMAT_SESSION 0x01
+#define CDROM_READ_TOC_EX_FORMAT_FULL_TOC 0x02
+#define CDROM_READ_TOC_EX_FORMAT_PMA 0x03
+#define CDROM_READ_TOC_EX_FORMAT_ATIP 0x04
+#define CDROM_READ_TOC_EX_FORMAT_CDTEXT 0x05
+
+typedef struct _CDROM_READ_TOC_EX {
+ UCHAR Format : 4;
+ UCHAR Reserved1 : 3;
+ UCHAR Msf : 1;
+ UCHAR SessionTrack;
+ UCHAR Reserved2;
+ UCHAR Reserved3;
+} CDROM_READ_TOC_EX, *PCDROM_READ_TOC_EX;
+
+typedef struct _CDROM_SEEK_AUDIO_MSF {
+ UCHAR M;
+ UCHAR S;
+ UCHAR F;
+} CDROM_SEEK_AUDIO_MSF, *PCDROM_SEEK_AUDIO_MSF;
+
+/* CDROM_SUB_Q_DATA_FORMAT.Format constants */
+#define IOCTL_CDROM_SUB_Q_CHANNEL 0x00
+#define IOCTL_CDROM_CURRENT_POSITION 0x01
+#define IOCTL_CDROM_MEDIA_CATALOG 0x02
+#define IOCTL_CDROM_TRACK_ISRC 0x03
+
+typedef struct _CDROM_SUB_Q_DATA_FORMAT {
+ UCHAR Format;
+ UCHAR Track;
+} CDROM_SUB_Q_DATA_FORMAT, *PCDROM_SUB_Q_DATA_FORMAT;
+
+typedef struct _CDROM_TOC {
+ UCHAR Length[2];
+ UCHAR FirstTrack;
+ UCHAR LastTrack;
+ TRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS];
+} CDROM_TOC, *PCDROM_TOC;
+
+#define CDROM_TOC_SIZE sizeof(CDROM_TOC)
+
+typedef struct _CDROM_TOC_ATIP_DATA_BLOCK {
+ UCHAR CdrwReferenceSpeed : 3;
+ UCHAR Reserved3 : 1;
+ UCHAR WritePower : 3;
+ UCHAR True1 : 1;
+ UCHAR Reserved4 : 6;
+ UCHAR UnrestrictedUse : 1;
+ UCHAR Reserved5 : 1;
+ UCHAR A3Valid : 1;
+ UCHAR A2Valid : 1;
+ UCHAR A1Valid : 1;
+ UCHAR Reserved6 : 3;
+ UCHAR IsCdrw : 1;
+ UCHAR True2 : 1;
+ UCHAR Reserved7;
+ UCHAR LeadInMsf[3];
+ UCHAR Reserved8;
+ UCHAR LeadOutMsf[3];
+ UCHAR Reserved9;
+ UCHAR A1Values[3];
+ UCHAR Reserved10;
+ UCHAR A2Values[3];
+ UCHAR Reserved11;
+ UCHAR A3Values[3];
+ UCHAR Reserved12;
+} CDROM_TOC_ATIP_DATA_BLOCK, *PCDROM_TOC_ATIP_DATA_BLOCK;
+
+#if 0 // Added to make MSVC happy.
+typedef struct _CDROM_TOC_ATIP_DATA {
+ UCHAR Length[2];
+ UCHAR Reserved1;
+ UCHAR Reserved2;
+ CDROM_TOC_ATIP_DATA_BLOCK Descriptors[0];
+ CDROM_TOC_ATIP_DATA_BLOCK Descriptors[1];
+} CDROM_TOC_ATIP_DATA, *PCDROM_TOC_ATIP_DATA;
+#endif
+
+/* CDROM_TOC_CD_TEXT_DATA_BLOCK.PackType constants */
+#define CDROM_CD_TEXT_PACK_ALBUM_NAME 0x80
+#define CDROM_CD_TEXT_PACK_PERFORMER 0x81
+#define CDROM_CD_TEXT_PACK_SONGWRITER 0x82
+#define CDROM_CD_TEXT_PACK_COMPOSER 0x83
+#define CDROM_CD_TEXT_PACK_ARRANGER 0x84
+#define CDROM_CD_TEXT_PACK_MESSAGES 0x85
+#define CDROM_CD_TEXT_PACK_DISC_ID 0x86
+#define CDROM_CD_TEXT_PACK_GENRE 0x87
+#define CDROM_CD_TEXT_PACK_TOC_INFO 0x88
+#define CDROM_CD_TEXT_PACK_TOC_INFO2 0x89
+#define CDROM_CD_TEXT_PACK_UPC_EAN 0x8e
+#define CDROM_CD_TEXT_PACK_SIZE_INFO 0x8f
+
+#if 0 // Added to make MSVC happy.
+typedef struct _CDROM_TOC_CD_TEXT_DATA_BLOCK {
+ UCHAR PackType;
+ UCHAR TrackNumber : 7;
+ UCHAR ExtensionFlag : 1;
+ UCHAR SequenceNumber;
+ UCHAR CharacterPosition : 4;
+ UCHAR BlockNumber : 3;
+ UCHAR Unicode : 1;
+ _ANONYMOUS_UNION union {
+ UCHAR Text[12];
+ WCHAR WText[6];
+ } DUMMYUNIONNAME;
+ UCHAR CRC[2];
+} CDROM_TOC_CD_TEXT_DATA_BLOCK, *PCDROM_TOC_CD_TEXT_DATA_BLOCK;
+
+typedef struct _CDROM_TOC_CD_TEXT_DATA {
+ UCHAR Length[2];
+ UCHAR Reserved1;
+ UCHAR Reserved2;
+ CDROM_TOC_CD_TEXT_DATA_BLOCK Descriptors[0];
+} CDROM_TOC_CD_TEXT_DATA, *PCDROM_TOC_CD_TEXT_DATA;
+#endif
+
+/* CDROM_TOC_FULL_TOC_DATA_BLOCK.Adr constants */
+#define ADR_NO_MODE_INFORMATION 0x0
+#define ADR_ENCODES_CURRENT_POSITION 0x1
+#define ADR_ENCODES_MEDIA_CATALOG 0x2
+#define ADR_ENCODES_ISRC 0x3
+
+typedef struct _CDROM_TOC_FULL_TOC_DATA_BLOCK {
+ UCHAR SessionNumber;
+ UCHAR Control : 4;
+ UCHAR Adr : 4;
+ UCHAR Reserved1;
+ UCHAR Point;
+ UCHAR MsfExtra[3];
+ UCHAR Zero;
+ UCHAR Msf[3];
+} CDROM_TOC_FULL_TOC_DATA_BLOCK, *PCDROM_TOC_FULL_TOC_DATA_BLOCK;
+
+#if 0 // Added to make MSVC happy.
+typedef struct _CDROM_TOC_FULL_TOC_DATA {
+ UCHAR Length[2];
+ UCHAR FirstCompleteSession;
+ UCHAR LastCompleteSession;
+ CDROM_TOC_FULL_TOC_DATA_BLOCK Descriptors[0];
+} CDROM_TOC_FULL_TOC_DATA, *PCDROM_TOC_FULL_TOC_DATA;
+
+typedef struct _CDROM_TOC_PMA_DATA {
+ UCHAR Length[2];
+ UCHAR Reserved1;
+ UCHAR Reserved2;
+ CDROM_TOC_FULL_TOC_DATA_BLOCK Descriptors[0];
+} CDROM_TOC_PMA_DATA, *PCDROM_TOC_PMA_DATA;
+#endif
+
+/* SUB_Q_HEADER.AudioStatus constants */
+#define AUDIO_STATUS_NOT_SUPPORTED 0x00
+#define AUDIO_STATUS_IN_PROGRESS 0x11
+#define AUDIO_STATUS_PAUSED 0x12
+#define AUDIO_STATUS_PLAY_COMPLETE 0x13
+#define AUDIO_STATUS_PLAY_ERROR 0x14
+#define AUDIO_STATUS_NO_STATUS 0x15
+
+typedef struct _SUB_Q_HEADER {
+ UCHAR Reserved;
+ UCHAR AudioStatus;
+ UCHAR DataLength[2];
+} SUB_Q_HEADER, *PSUB_Q_HEADER;
+
+typedef struct _SUB_Q_MEDIA_CATALOG_NUMBER {
+ SUB_Q_HEADER Header;
+ UCHAR FormatCode;
+ UCHAR Reserved[3];
+ UCHAR Reserved1 : 7;
+ UCHAR Mcval :1;
+ UCHAR MediaCatalog[15];
+} SUB_Q_MEDIA_CATALOG_NUMBER, *PSUB_Q_MEDIA_CATALOG_NUMBER;
+
+typedef struct _SUB_Q_TRACK_ISRC {
+ SUB_Q_HEADER Header;
+ UCHAR FormatCode;
+ UCHAR Reserved0;
+ UCHAR Track;
+ UCHAR Reserved1;
+ UCHAR Reserved2 : 7;
+ UCHAR Tcval : 1;
+ UCHAR TrackIsrc[15];
+} SUB_Q_TRACK_ISRC, *PSUB_Q_TRACK_ISRC;
+
+typedef struct _SUB_Q_CURRENT_POSITION {
+ SUB_Q_HEADER Header;
+ UCHAR FormatCode;
+ UCHAR Control : 4;
+ UCHAR ADR : 4;
+ UCHAR TrackNumber;
+ UCHAR IndexNumber;
+ UCHAR AbsoluteAddress[4];
+ UCHAR TrackRelativeAddress[4];
+} SUB_Q_CURRENT_POSITION, *PSUB_Q_CURRENT_POSITION;
+
+typedef union _SUB_Q_CHANNEL_DATA {
+ SUB_Q_CURRENT_POSITION CurrentPosition;
+ SUB_Q_MEDIA_CATALOG_NUMBER MediaCatalog;
+ SUB_Q_TRACK_ISRC TrackIsrc;
+} SUB_Q_CHANNEL_DATA, *PSUB_Q_CHANNEL_DATA;
+
+/* CDROM_AUDIO_CONTROL.LbaFormat constants */
+#define AUDIO_WITH_PREEMPHASIS 0x1
+#define DIGITAL_COPY_PERMITTED 0x2
+#define AUDIO_DATA_TRACK 0x4
+#define TWO_FOUR_CHANNEL_AUDIO 0x8
+
+typedef struct _CDROM_AUDIO_CONTROL {
+ UCHAR LbaFormat;
+ USHORT LogicalBlocksPerSecond;
+} CDROM_AUDIO_CONTROL, *PCDROM_AUDIO_CONTROL;
+
+typedef struct _VOLUME_CONTROL {
+ UCHAR PortVolume[4];
+} VOLUME_CONTROL, *PVOLUME_CONTROL;
+
+typedef enum _TRACK_MODE_TYPE {
+ YellowMode2,
+ XAForm2,
+ CDDA
+} TRACK_MODE_TYPE, *PTRACK_MODE_TYPE;
+
+typedef struct __RAW_READ_INFO {
+ LARGE_INTEGER DiskOffset;
+ ULONG SectorCount;
+ TRACK_MODE_TYPE TrackMode;
+} RAW_READ_INFO, *PRAW_READ_INFO;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NTDDCDRM_H */
diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp
new file mode 100644
index 0000000000..6c057efdb7
--- /dev/null
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -0,0 +1,388 @@
+/* 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.
+ *
+ * Original license header:
+ *
+ * Cabal - Legacy Game Implementations
+ *
+ * Cabal 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.
+ *
+ */
+
+#ifdef WIN32
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef ARRAYSIZE // winnt.h defines ARRAYSIZE, but we want our own one...
+
+#include "backends/audiocd/win32/win32-audiocd.h"
+
+#include "audio/audiostream.h"
+#include "backends/audiocd/audiocd-stream.h"
+#include "backends/audiocd/default/default-audiocd.h"
+#include "common/array.h"
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/mutex.h"
+#include "common/queue.h"
+#include "common/str.h"
+#include "common/timer.h"
+
+#include <winioctl.h>
+#if _MSC_VER < 1900
+// WORKAROUND: Older versions of MSVC might not supply DDK headers by default.
+// Visual Studio 2015 contains the required headers. We use a compatability
+// header from MinGW's w32api for all older versions.
+// TODO: Limit this to the Visual Studio versions which actually require this.
+#include "msvc/ntddcdrm.h"
+#elif defined(__MINGW32__) && !defined(__MINGW64__)
+// Classic MinGW uses non standard paths for DDK headers.
+#include <ddk/ntddcdrm.h>
+#else
+#include <ntddcdrm.h>
+#endif
+
+class Win32AudioCDStream : public AudioCDStream {
+public:
+ Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry);
+ ~Win32AudioCDStream();
+
+protected:
+ uint getStartFrame() const;
+ uint getEndFrame() const;
+ bool readFrame(int frame, int16 *buffer);
+
+private:
+ HANDLE _driveHandle;
+ const TRACK_DATA &_startEntry, &_endEntry;
+
+ enum {
+ // The CD-ROM pre-gap is 2s
+ kPreGapFrames = kFramesPerSecond * 2
+ };
+
+ static int getFrameCount(const TRACK_DATA &data) {
+ int time = data.Address[1];
+ time *= kSecondsPerMinute;
+ time += data.Address[2];
+ time *= kFramesPerSecond;
+ time += data.Address[3];
+ return time;
+ }
+};
+
+Win32AudioCDStream::Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry) :
+ _driveHandle(handle), _startEntry(startEntry), _endEntry(endEntry) {
+ // We fill the buffer here already to prevent any out of sync issues due
+ // to the CD not yet having spun up.
+ startTimer(true);
+}
+
+Win32AudioCDStream::~Win32AudioCDStream() {
+ stopTimer();
+}
+
+uint Win32AudioCDStream::getStartFrame() const {
+ return getFrameCount(_startEntry);
+}
+
+uint Win32AudioCDStream::getEndFrame() const {
+ return getFrameCount(_endEntry);
+}
+
+bool Win32AudioCDStream::readFrame(int frame, int16 *buffer) {
+ // Request to read that frame
+ RAW_READ_INFO readAudio;
+ memset(&readAudio, 0, sizeof(readAudio));
+ readAudio.DiskOffset.QuadPart = (frame - kPreGapFrames) * 2048;
+ readAudio.SectorCount = 1;
+ readAudio.TrackMode = CDDA;
+
+ DWORD bytesReturned;
+ return DeviceIoControl(
+ _driveHandle,
+ IOCTL_CDROM_RAW_READ,
+ &readAudio,
+ sizeof(readAudio),
+ buffer,
+ kBytesPerFrame,
+ &bytesReturned,
+ NULL);
+}
+
+
+class Win32AudioCDManager : public DefaultAudioCDManager {
+public:
+ Win32AudioCDManager();
+ ~Win32AudioCDManager();
+
+ bool open();
+ void close();
+ bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false);
+
+protected:
+ bool openCD(int drive);
+ bool openCD(const Common::String &drive);
+
+private:
+ bool loadTOC();
+
+ typedef Common::Array<char> DriveList;
+ DriveList detectDrives();
+ bool tryAddDrive(char drive, DriveList &drives);
+
+ HANDLE _driveHandle;
+ int _firstTrack, _lastTrack;
+ Common::Array<TRACK_DATA> _tocEntries;
+};
+
+Win32AudioCDManager::Win32AudioCDManager() {
+ _driveHandle = INVALID_HANDLE_VALUE;
+ _firstTrack = _lastTrack = 0;
+}
+
+Win32AudioCDManager::~Win32AudioCDManager() {
+ close();
+}
+
+bool Win32AudioCDManager::open() {
+ close();
+
+ if (openRealCD())
+ return true;
+
+ return DefaultAudioCDManager::open();
+}
+
+bool Win32AudioCDManager::openCD(int drive) {
+ // Fetch the drive list
+ DriveList drives = detectDrives();
+ if (drive >= (int)drives.size())
+ return false;
+
+ debug(1, "Opening CD drive %c:\\", drives[drive]);
+
+ // Construct the drive path and try to open it
+ Common::String drivePath = Common::String::format("\\\\.\\%c:", drives[drive]);
+ _driveHandle = CreateFileA(drivePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ if (_driveHandle == INVALID_HANDLE_VALUE) {
+ warning("Failed to open drive %c:\\, error %d", drives[drive], (int)GetLastError());
+ return false;
+ }
+
+ if (!loadTOC()) {
+ close();
+ return false;
+ }
+
+ return true;
+}
+
+bool Win32AudioCDManager::openCD(const Common::String &drive) {
+ // Just some bounds checking
+ if (drive.empty() || drive.size() > 3)
+ return false;
+
+ if (!Common::isAlpha(drive[0]) || drive[1] != ':')
+ return false;
+
+ if (drive[2] != 0 && drive[2] != '\\')
+ return false;
+
+ DriveList drives;
+ if (!tryAddDrive(toupper(drive[0]), drives))
+ return false;
+
+ // Construct the drive path and try to open it
+ Common::String drivePath = Common::String::format("\\\\.\\%c:", drives[0]);
+ _driveHandle = CreateFileA(drivePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ if (_driveHandle == INVALID_HANDLE_VALUE) {
+ warning("Failed to open drive %c:\\, error %d", drives[0], (int)GetLastError());
+ return false;
+ }
+
+ if (!loadTOC()) {
+ close();
+ return false;
+ }
+
+ return true;
+}
+
+void Win32AudioCDManager::close() {
+ DefaultAudioCDManager::close();
+
+ if (_driveHandle != INVALID_HANDLE_VALUE) {
+ CloseHandle(_driveHandle);
+ _driveHandle = INVALID_HANDLE_VALUE;
+ }
+
+ _firstTrack = _lastTrack = 0;
+ _tocEntries.clear();
+}
+
+bool Win32AudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate) {
+ // Prefer emulation
+ if (DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate))
+ return true;
+
+ // If we're set to only emulate, or have no CD drive, return here
+ if (onlyEmulate || _driveHandle == INVALID_HANDLE_VALUE)
+ return false;
+
+ // HACK: For now, just assume that track number is right
+ // That only works because ScummVM uses the wrong track number anyway
+
+ if (track >= (int)_tocEntries.size() - 1) {
+ warning("No such track %d", track);
+ return false;
+ }
+
+ // Bail if the track isn't an audio track
+ if ((_tocEntries[track].Control & 0x04) != 0) {
+ warning("Track %d is not audio", track);
+ return false;
+ }
+
+ // Create the AudioStream and play it
+ debug(1, "Playing CD track %d", track);
+
+ Audio::SeekableAudioStream *audioStream = new Win32AudioCDStream(_driveHandle, _tocEntries[track], _tocEntries[track + 1]);
+
+ Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75);
+ Audio::Timestamp end = (duration == 0) ? audioStream->getLength() : Audio::Timestamp(0, startFrame + duration, 75);
+
+ // Fake emulation since we're really playing an AudioStream
+ _emulating = true;
+
+ _mixer->playStream(
+ Audio::Mixer::kMusicSoundType,
+ &_handle,
+ Audio::makeLoopingAudioStream(audioStream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops),
+ -1,
+ _cd.volume,
+ _cd.balance,
+ DisposeAfterUse::YES,
+ true);
+ return true;
+}
+
+bool Win32AudioCDManager::loadTOC() {
+ CDROM_READ_TOC_EX tocRequest;
+ memset(&tocRequest, 0, sizeof(tocRequest));
+ tocRequest.Format = CDROM_READ_TOC_EX_FORMAT_TOC;
+ tocRequest.Msf = 1;
+ tocRequest.SessionTrack = 0;
+
+ DWORD bytesReturned;
+ CDROM_TOC tocData;
+ bool result = DeviceIoControl(
+ _driveHandle,
+ IOCTL_CDROM_READ_TOC_EX,
+ &tocRequest,
+ sizeof(tocRequest),
+ &tocData,
+ sizeof(tocData),
+ &bytesReturned,
+ NULL);
+ if (!result) {
+ debug("Failed to query the CD TOC: %d", (int)GetLastError());
+ return false;
+ }
+
+ _firstTrack = tocData.FirstTrack;
+ _lastTrack = tocData.LastTrack;
+#if 0
+ debug("First Track: %d", tocData.FirstTrack);
+ debug("Last Track: %d", tocData.LastTrack);
+#endif
+
+ for (uint32 i = 0; i < (bytesReturned - 4) / sizeof(TRACK_DATA); i++)
+ _tocEntries.push_back(tocData.TrackData[i]);
+
+#if 0
+ for (uint32 i = 0; i < _tocEntries.size(); i++) {
+ const TRACK_DATA &entry = _tocEntries[i];
+ debug("Entry:");
+ debug("\tTrack: %d", entry.TrackNumber);
+ debug("\tAdr: %d", entry.Adr);
+ debug("\tCtrl: %d", entry.Control);
+ debug("\tMSF: %d:%d:%d\n", entry.Address[1], entry.Address[2], entry.Address[3]);
+ }
+#endif
+
+ return true;
+}
+
+Win32AudioCDManager::DriveList Win32AudioCDManager::detectDrives() {
+ DriveList drives;
+
+ // Try to get the game path's drive
+ char gameDrive = 0;
+ if (ConfMan.hasKey("path")) {
+ Common::String gamePath = ConfMan.get("path");
+ char fullPath[MAX_PATH];
+ DWORD result = GetFullPathNameA(gamePath.c_str(), sizeof(fullPath), fullPath, 0);
+
+ if (result > 0 && result < sizeof(fullPath) && Common::isAlpha(fullPath[0]) && fullPath[1] == ':' && tryAddDrive(toupper(fullPath[0]), drives))
+ gameDrive = drives[0];
+ }
+
+ // Try adding the rest of the drives
+ for (char drive = 'A'; drive <= 'Z'; drive++)
+ if (drive != gameDrive)
+ tryAddDrive(drive, drives);
+
+ return drives;
+}
+
+bool Win32AudioCDManager::tryAddDrive(char drive, DriveList &drives) {
+ Common::String drivePath = Common::String::format("%c:\\", drive);
+
+ // Ensure it's an actual CD drive
+ if (GetDriveTypeA(drivePath.c_str()) != DRIVE_CDROM)
+ return false;
+
+ debug(2, "Detected drive %c:\\ as a CD drive", drive);
+ drives.push_back(drive);
+ return true;
+}
+
+AudioCDManager *createWin32AudioCDManager() {
+ return new Win32AudioCDManager();
+}
+
+#endif // WIN32 \ No newline at end of file
diff --git a/backends/audiocd/win32/win32-audiocd.h b/backends/audiocd/win32/win32-audiocd.h
new file mode 100644
index 0000000000..0c103641ef
--- /dev/null
+++ b/backends/audiocd/win32/win32-audiocd.h
@@ -0,0 +1,60 @@
+/* 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.
+ *
+ * Original license header:
+ *
+ * Cabal - Legacy Game Implementations
+ *
+ * Cabal 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.
+ *
+ */
+
+#ifndef BACKENDS_AUDIOCD_WIN32_H
+#define BACKENDS_AUDIOCD_WIN32_H
+
+#ifdef WIN32
+
+class AudioCDManager;
+
+/**
+ * Create an AudioCDManager using the Win32 API
+ */
+AudioCDManager *createWin32AudioCDManager();
+
+#endif
+
+#endif
+
diff --git a/backends/module.mk b/backends/module.mk
index 3d412c031a..3f7620e2a8 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -3,6 +3,7 @@ MODULE := backends
MODULE_OBJS := \
base-backend.o \
modular-backend.o \
+ audiocd/audiocd-stream.o \
audiocd/default/default-audiocd.o \
events/default/default-events.o \
fs/abstract-fs.o \
@@ -97,6 +98,7 @@ endif
ifdef MACOSX
MODULE_OBJS += \
+ audiocd/macosx/macosx-audiocd.o \
midi/coreaudio.o \
midi/coremidi.o \
updates/macosx/macosx-updates.o \
@@ -105,6 +107,7 @@ endif
ifdef WIN32
MODULE_OBJS += \
+ audiocd/win32/win32-audiocd.o \
fs/windows/windows-fs.o \
fs/windows/windows-fs-factory.o \
midi/windows.o \
@@ -128,6 +131,11 @@ MODULE_OBJS += \
events/ps3sdl/ps3sdl-events.o
endif
+ifdef USE_LINUXCD
+MODULE_OBJS += \
+ audiocd/linux/linux-audiocd.o
+endif
+
ifeq ($(BACKEND),tizen)
MODULE_OBJS += \
timer/tizen/timer.o
diff --git a/backends/platform/dc/dc.h b/backends/platform/dc/dc.h
index d8ab549c3a..b567142b8f 100644
--- a/backends/platform/dc/dc.h
+++ b/backends/platform/dc/dc.h
@@ -57,21 +57,16 @@ class DCHardware {
};
class DCCDManager : public DefaultAudioCDManager {
- // Initialize the specified CD drive for audio playback.
- bool openCD(int drive);
-
- // Poll cdrom status
- // Returns true if cd audio is playing
- bool pollCD();
-
- // Play cdrom audio track
- void playCD(int track, int num_loops, int start_frame, int duration);
+public:
+ // Poll cdrom status
+ // Returns true if cd audio is playing
+ bool isPlaying() const;
- // Stop cdrom audio track
- void stopCD();
+ // Play cdrom audio track
+ void play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false);
- // Update cdrom audio status
- void updateCD();
+ // Stop cdrom audio track
+ void stop();
};
class OSystem_Dreamcast : private DCHardware, public EventsBaseBackend, public PaletteManager, public FilesystemFactory
diff --git a/backends/platform/dc/dcmain.cpp b/backends/platform/dc/dcmain.cpp
index eede796991..bd66b81b35 100644
--- a/backends/platform/dc/dcmain.cpp
+++ b/backends/platform/dc/dcmain.cpp
@@ -90,43 +90,45 @@ static bool find_track(int track, int &first_sec, int &last_sec)
return false;
}
-void DCCDManager::playCD(int track, int num_loops, int start_frame, int duration)
-{
- int first_sec, last_sec;
+void DCCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate) {
+ DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate);
+
+ // If we're playing now, are set to only emulate, return here
+ if (isPlaying() || onlyEmulate)
+ return;
+
+ int firstSec, lastSec;
#if 1
- if (num_loops)
- --num_loops;
+ if (numLoops)
+ --numLoops;
#endif
- if (num_loops>14) num_loops=14;
- else if (num_loops<0) num_loops=15; // infinity
- if (!find_track(track, first_sec, last_sec))
- return;
- if (duration)
- last_sec = first_sec + start_frame + duration;
- first_sec += start_frame;
- play_cdda_sectors(first_sec, last_sec, num_loops);
-}
-void DCCDManager::stopCD()
-{
- stop_cdda();
-}
+ if (numLoops > 14)
+ numLoops = 14;
+ else if (numLoops < 0)
+ num_loops = 15; // infinity
-bool DCCDManager::pollCD()
-{
- extern int getCdState();
- return getCdState() == 3;
+ if (!find_track(track, firstSec, lastSec))
+ return;
+
+ if (duration)
+ lastSec = firstSec + startFrame + duration;
+
+ firstSec += startFrame;
+ play_cdda_sectors(firstSec, lastSec, numLoops);
}
-void DCCDManager::updateCD()
-{
- // Dummy. The CD drive takes care of itself.
+void DCCDManager::stop() {
+ DefaultAudioCDManager::stop();
+ stop_cdda();
}
-bool DCCDManager::openCD(int drive)
-{
- // Dummy.
- return true;
+bool DCCDManager::isPlaying() const {
+ if (DefaultAudioCDManager::isPlaying())
+ return true;
+
+ extern int getCdState();
+ return getCdState() == 3;
}
void OSystem_Dreamcast::setWindowCaption(const char *caption)
diff --git a/backends/platform/ds/arm9/source/osystem_ds.cpp b/backends/platform/ds/arm9/source/osystem_ds.cpp
index c53f57523d..f23192cd9d 100644
--- a/backends/platform/ds/arm9/source/osystem_ds.cpp
+++ b/backends/platform/ds/arm9/source/osystem_ds.cpp
@@ -715,7 +715,7 @@ void OSystem_DS::deleteMutex(MutexRef mutex) {
// and should be replaced by an AudioCDManager subclass,
// see backends/audiocd/ and common/system.h
-bool OSystem_DS::openCD(int drive) {
+bool OSystem_DS::openCD() {
return DS::CD::checkCD();
}
diff --git a/backends/platform/ds/arm9/source/osystem_ds.h b/backends/platform/ds/arm9/source/osystem_ds.h
index f4dbac66f7..9f73e125c2 100644
--- a/backends/platform/ds/arm9/source/osystem_ds.h
+++ b/backends/platform/ds/arm9/source/osystem_ds.h
@@ -130,7 +130,8 @@ public:
// FIXME/TODO: The CD API as follows is *obsolete*
// and should be replaced by an AudioCDManager subclass,
// see backends/audiocd/ and common/system.h
- virtual bool openCD(int drive);
+ virtual bool openCD();
+ virtual void closeCD() {}
virtual bool pollCD();
virtual void playCD(int track, int num_loops, int start_frame, int duration);
virtual void stopCD();
diff --git a/backends/platform/sdl/macosx/macosx.cpp b/backends/platform/sdl/macosx/macosx.cpp
index 38a2d7441c..7652c0d833 100644
--- a/backends/platform/sdl/macosx/macosx.cpp
+++ b/backends/platform/sdl/macosx/macosx.cpp
@@ -27,9 +27,10 @@
#ifdef MACOSX
-#include "backends/platform/sdl/macosx/macosx.h"
+#include "backends/audiocd/macosx/macosx-audiocd.h"
#include "backends/mixer/doublebuffersdl/doublebuffersdl-mixer.h"
#include "backends/platform/sdl/macosx/appmenu_osx.h"
+#include "backends/platform/sdl/macosx/macosx.h"
#include "backends/updates/macosx/macosx-updates.h"
#include "backends/taskbar/macosx/macosx-taskbar.h"
@@ -170,4 +171,8 @@ Common::String OSystem_MacOSX::getSystemLanguage() const {
#endif // USE_DETECTLANG
}
+AudioCDManager *OSystem_MacOSX::createAudioCDManager() {
+ return createMacOSXAudioCDManager();
+}
+
#endif
diff --git a/backends/platform/sdl/macosx/macosx.h b/backends/platform/sdl/macosx/macosx.h
index c8b4beaeec..6905284a5f 100644
--- a/backends/platform/sdl/macosx/macosx.h
+++ b/backends/platform/sdl/macosx/macosx.h
@@ -38,6 +38,11 @@ public:
virtual void init();
virtual void initBackend();
virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0);
+
+protected:
+ // Override createAudioCDManager() to get our Mac-specific
+ // version.
+ virtual AudioCDManager *createAudioCDManager();
};
#endif
diff --git a/backends/platform/sdl/posix/posix.cpp b/backends/platform/sdl/posix/posix.cpp
index 525c74a91a..e2a642b288 100644
--- a/backends/platform/sdl/posix/posix.cpp
+++ b/backends/platform/sdl/posix/posix.cpp
@@ -36,6 +36,11 @@
#include "backends/fs/posix/posix-fs.h"
#include "backends/taskbar/unity/unity-taskbar.h"
+#ifdef USE_LINUXCD
+#include "backends/audiocd/linux/linux-audiocd.h"
+#endif
+
+#include <errno.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -239,4 +244,12 @@ bool OSystem_POSIX::displayLogFile() {
}
+AudioCDManager *OSystem_POSIX::createAudioCDManager() {
+#ifdef USE_LINUXCD
+ return createLinuxAudioCDManager();
+#else
+ return OSystem_SDL::createAudioCDManager();
+#endif
+}
+
#endif
diff --git a/backends/platform/sdl/posix/posix.h b/backends/platform/sdl/posix/posix.h
index f67515ddb3..0514d30191 100644
--- a/backends/platform/sdl/posix/posix.h
+++ b/backends/platform/sdl/posix/posix.h
@@ -59,6 +59,8 @@ protected:
virtual Common::String getDefaultConfigFileName();
virtual Common::WriteStream *createLogFile();
+
+ virtual AudioCDManager *createAudioCDManager();
};
#endif
diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index fffb9d56d9..c55753194b 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -245,15 +245,7 @@ void OSystem_SDL::initBackend() {
_timerManager = new SdlTimerManager();
#endif
- if (_audiocdManager == 0) {
- // Audio CD support was removed with SDL 2.0
-#if SDL_VERSION_ATLEAST(2, 0, 0)
- _audiocdManager = new DefaultAudioCDManager();
-#else
- _audiocdManager = new SdlAudioCDManager();
-#endif
-
- }
+ _audiocdManager = createAudioCDManager();
// Setup a custom program icon.
_window->setupIcon();
@@ -491,6 +483,15 @@ Common::TimerManager *OSystem_SDL::getTimerManager() {
#endif
}
+AudioCDManager *OSystem_SDL::createAudioCDManager() {
+ // Audio CD support was removed with SDL 2.0
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+ return new DefaultAudioCDManager();
+#else
+ return new SdlAudioCDManager();
+#endif
+}
+
#ifdef USE_OPENGL
const OSystem::GraphicsMode *OSystem_SDL::getSupportedGraphicsModes() const {
diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h
index 5ee56d0568..c93c8308a7 100644
--- a/backends/platform/sdl/sdl.h
+++ b/backends/platform/sdl/sdl.h
@@ -104,6 +104,11 @@ protected:
*/
virtual void initSDL();
+ /**
+ * Create the audio CD manager
+ */
+ virtual AudioCDManager *createAudioCDManager();
+
// Logging
virtual Common::WriteStream *createLogFile() { return 0; }
Backends::Log::Log *_logger;
diff --git a/backends/platform/sdl/win32/win32.cpp b/backends/platform/sdl/win32/win32.cpp
index 0f70c00b40..fbab7eb782 100644
--- a/backends/platform/sdl/win32/win32.cpp
+++ b/backends/platform/sdl/win32/win32.cpp
@@ -35,6 +35,7 @@
#include "common/error.h"
#include "common/textconsole.h"
+#include "backends/audiocd/win32/win32-audiocd.h"
#include "backends/platform/sdl/win32/win32.h"
#include "backends/platform/sdl/win32/win32-window.h"
#include "backends/saves/windows/windows-saves.h"
@@ -318,4 +319,8 @@ void OSystem_Win32::addSysArchivesToSearchSet(Common::SearchSet &s, int priority
OSystem_SDL::addSysArchivesToSearchSet(s, priority);
}
+AudioCDManager *OSystem_Win32::createAudioCDManager() {
+ return createWin32AudioCDManager();
+}
+
#endif
diff --git a/backends/platform/sdl/win32/win32.h b/backends/platform/sdl/win32/win32.h
index 473e78ff0b..ca0843e834 100644
--- a/backends/platform/sdl/win32/win32.h
+++ b/backends/platform/sdl/win32/win32.h
@@ -49,6 +49,10 @@ protected:
virtual Common::String getDefaultConfigFileName();
virtual Common::WriteStream *createLogFile();
+
+ // Override createAudioCDManager() to get our Mac-specific
+ // version.
+ virtual AudioCDManager *createAudioCDManager();
};
#endif
diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index 105d810460..a9116bf5f2 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -100,8 +100,9 @@ static const char HELP_STRING[] =
" -u, --dump-scripts Enable script dumping if a directory called 'dumps'\n"
" exists in the current directory\n"
"\n"
- " --cdrom=NUM CD drive to play CD audio from (default: 0 = first\n"
- " drive)\n"
+ " --cdrom=DRIVE CD drive to play CD audio from; can either be a\n"
+ " drive, path, or numeric index (default: 0 = best\n"
+ " choice drive)\n"
" --joystick[=NUM] Enable joystick input (default: 0 = first joystick)\n"
" --platform=WORD Specify platform of game (allowed values: 2gs, 3do,\n"
" acorn, amiga, atari, c64, fmtowns, nes, mac, pc, pc98,\n"
diff --git a/configure b/configure
index 0e7a5a9b56..c958e9acf0 100755
--- a/configure
+++ b/configure
@@ -4259,6 +4259,20 @@ define_in_config_if_yes "$_opengl" "USE_OPENGL"
define_in_config_if_yes "$_opengles" "USE_GLES"
#
+# Check for Linux CD-ROM support
+#
+echocheck "Linux CD-ROM"
+linuxcd=no
+cat > $TMPC << EOF
+#include <linux/cdrom.h>
+int main(void) { int x = CDROMREADAUDIO; return 0; }
+EOF
+cc_check && linuxcd=yes
+define_in_config_if_yes "$linuxcd" 'USE_LINUXCD'
+echo "$linuxcd"
+
+
+#
# Check for nasm
#
if test "$_have_x86" = yes ; then
diff --git a/engines/agos/event.cpp b/engines/agos/event.cpp
index 95bcc68234..5240cdd771 100644
--- a/engines/agos/event.cpp
+++ b/engines/agos/event.cpp
@@ -427,7 +427,7 @@ void AGOSEngine::delay(uint amount) {
uint32 cur = start;
uint this_delay, vgaPeriod;
- _system->getAudioCDManager()->updateCD();
+ _system->getAudioCDManager()->update();
_debugger->onFrame();
@@ -538,7 +538,7 @@ void AGOSEngine::delay(uint amount) {
if (_leftButton == 1)
_leftButtonCount++;
- _system->getAudioCDManager()->updateCD();
+ _system->getAudioCDManager()->update();
_system->updateScreen();
diff --git a/engines/agos/res_snd.cpp b/engines/agos/res_snd.cpp
index d04f1735d6..3a092e652c 100644
--- a/engines/agos/res_snd.cpp
+++ b/engines/agos/res_snd.cpp
@@ -228,7 +228,7 @@ void AGOSEngine_Simon1::playMusic(uint16 music, uint16 track) {
// Support for compressed music from the ScummVM Music Enhancement Project
_system->getAudioCDManager()->stop();
- _system->getAudioCDManager()->play(music + 1, -1, 0, 0);
+ _system->getAudioCDManager()->play(music + 1, -1, 0, 0, true);
if (_system->getAudioCDManager()->isPlaying())
return;
diff --git a/engines/cine/main_loop.cpp b/engines/cine/main_loop.cpp
index e52fc464d5..19a2d8a82e 100644
--- a/engines/cine/main_loop.cpp
+++ b/engines/cine/main_loop.cpp
@@ -222,7 +222,7 @@ void manageEvents() {
mouseData.left = mouseLeft;
mouseData.right = mouseRight;
- g_system->getAudioCDManager()->updateCD();
+ g_system->getAudioCDManager()->update();
}
void getMouseData(uint16 param, uint16 *pButton, uint16 *pX, uint16 *pY) {
diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp
index 7cab067371..a8b4c085ff 100644
--- a/engines/cine/sound.cpp
+++ b/engines/cine/sound.cpp
@@ -939,6 +939,10 @@ PCSound::PCSound(Audio::Mixer *mixer, CineEngine *vm)
}
_player = new PCSoundFxPlayer(_soundDriver);
+
+ // Ensure the CD is open
+ if (_vm->getGameType() == GType_FW && (_vm->getFeatures() & GF_CD))
+ g_system->getAudioCDManager()->open();
}
PCSound::~PCSound() {
diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp
index c72d77c281..9ac9031fb7 100644
--- a/engines/drascula/drascula.cpp
+++ b/engines/drascula/drascula.cpp
@@ -183,9 +183,7 @@ DrasculaEngine::DrasculaEngine(OSystem *syst, const DrasculaGameDescription *gam
const Common::FSNode gameDataDir(ConfMan.get("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "audio");
- int cd_num = ConfMan.getInt("cdrom");
- if (cd_num >= 0)
- _system->getAudioCDManager()->openCD(cd_num);
+ _system->getAudioCDManager()->open();
_lang = kEnglish;
diff --git a/engines/drascula/sound.cpp b/engines/drascula/sound.cpp
index 148dae76f5..c576b37660 100644
--- a/engines/drascula/sound.cpp
+++ b/engines/drascula/sound.cpp
@@ -133,7 +133,7 @@ void DrasculaEngine::stopMusic() {
}
void DrasculaEngine::updateMusic() {
- _system->getAudioCDManager()->updateCD();
+ _system->getAudioCDManager()->update();
}
int DrasculaEngine::musicStatus() {
diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp
index 24bdb858d8..d995f26d9f 100644
--- a/engines/gob/gob.cpp
+++ b/engines/gob/gob.cpp
@@ -296,9 +296,7 @@ Common::Error GobEngine::run() {
if (isCD())
checkCD();
- int cd_num = ConfMan.getInt("cdrom");
- if (cd_num >= 0)
- _system->getAudioCDManager()->openCD(cd_num);
+ _system->getAudioCDManager()->open();
_global->_debugFlag = 1;
_video->_doRangeClamp = true;
diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp
index 2021cef6e8..bbc290eccf 100644
--- a/engines/groovie/groovie.cpp
+++ b/engines/groovie/groovie.cpp
@@ -257,11 +257,7 @@ Common::Error GroovieEngine::run() {
// the same cd
if (getPlatform() != Common::kPlatformIOS) {
checkCD();
-
- // Initialize the CD
- int cd_num = ConfMan.getInt("cdrom");
- if (cd_num >= 0)
- _system->getAudioCDManager()->openCD(cd_num);
+ _system->getAudioCDManager()->open();
}
while (!shouldQuit()) {
diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp
index 65ab4f31ef..646f908b94 100644
--- a/engines/kyra/sound_towns.cpp
+++ b/engines/kyra/sound_towns.cpp
@@ -68,11 +68,14 @@ bool SoundTowns::init() {
_player->driver()->intf()->callback(70, 0x33);*/
_player->driver()->setOutputVolume(1, 118, 118);
+ // Initialize CD for audio
+ g_system->getAudioCDManager()->open();
+
return true;
}
void SoundTowns::process() {
- g_system->getAudioCDManager()->updateCD();
+ g_system->getAudioCDManager()->update();
}
void SoundTowns::playTrack(uint8 track) {
@@ -95,7 +98,7 @@ void SoundTowns::playTrack(uint8 track) {
if (_musicEnabled == 2 && trackNum != -1) {
_player->driver()->setOutputVolume(1, 118, 118);
g_system->getAudioCDManager()->play(trackNum + 1, loop ? -1 : 1, 0, 0);
- g_system->getAudioCDManager()->updateCD();
+ g_system->getAudioCDManager()->update();
_cdaPlaying = true;
} else if (_musicEnabled) {
playEuphonyTrack(READ_LE_UINT32(&res()->cdaTable[tTableIndex]), loop);
@@ -108,7 +111,7 @@ void SoundTowns::playTrack(uint8 track) {
void SoundTowns::haltTrack() {
_lastTrack = -1;
g_system->getAudioCDManager()->stop();
- g_system->getAudioCDManager()->updateCD();
+ g_system->getAudioCDManager()->update();
_cdaPlaying = false;
for (int i = 0; i < 6; i++)
@@ -407,6 +410,10 @@ bool SoundPC98::init() {
_driver = new TownsPC98_AudioDriver(_mixer, TownsPC98_AudioDriver::kType26);
bool reslt = _driver->init();
updateVolumeSettings();
+
+ // Initialize CD for audio
+ g_system->getAudioCDManager()->open();
+
return reslt;
}
@@ -471,7 +478,7 @@ void SoundPC98::playTrack(uint8 track) {
void SoundPC98::haltTrack() {
_lastTrack = -1;
g_system->getAudioCDManager()->stop();
- g_system->getAudioCDManager()->updateCD();
+ g_system->getAudioCDManager()->update();
_driver->reset();
}
@@ -529,6 +536,10 @@ bool SoundTownsPC98_v2::init() {
if (_resInfo[_currentResourceSet])
if (_resInfo[_currentResourceSet]->cdaTableSize)
_vm->checkCD();
+
+ // Initialize CD for audio
+ bool hasRealCD = g_system->getAudioCDManager()->open();
+
// FIXME: While checking for 'track1.XXX(X)' looks like
// a good idea, we should definitely not be doing this
// here. Basically our filenaming scheme could change
@@ -538,7 +549,7 @@ bool SoundTownsPC98_v2::init() {
// check if we have access to CD audio.
Resource *r = _vm->resource();
if (_musicEnabled &&
- (r->exists("track1.mp3") || r->exists("track1.ogg") || r->exists("track1.flac") || r->exists("track1.fla")
+ (hasRealCD || r->exists("track1.mp3") || r->exists("track1.ogg") || r->exists("track1.flac") || r->exists("track1.fla")
|| r->exists("track01.mp3") || r->exists("track01.ogg") || r->exists("track01.flac") || r->exists("track01.fla")))
_musicEnabled = 2;
else
@@ -580,7 +591,7 @@ void SoundTownsPC98_v2::loadSoundFile(Common::String file) {
}
void SoundTownsPC98_v2::process() {
- g_system->getAudioCDManager()->updateCD();
+ g_system->getAudioCDManager()->update();
}
void SoundTownsPC98_v2::playTrack(uint8 track) {
@@ -610,7 +621,7 @@ void SoundTownsPC98_v2::playTrack(uint8 track) {
if (_musicEnabled == 2 && trackNum != -1) {
g_system->getAudioCDManager()->play(trackNum+1, _driver->looping() ? -1 : 1, 0, 0);
- g_system->getAudioCDManager()->updateCD();
+ g_system->getAudioCDManager()->update();
} else if (_musicEnabled) {
_driver->cont();
}
@@ -621,7 +632,7 @@ void SoundTownsPC98_v2::playTrack(uint8 track) {
void SoundTownsPC98_v2::haltTrack() {
_lastTrack = -1;
g_system->getAudioCDManager()->stop();
- g_system->getAudioCDManager()->updateCD();
+ g_system->getAudioCDManager()->update();
_driver->reset();
}
diff --git a/engines/made/made.cpp b/engines/made/made.cpp
index ab07ef757b..f1539297ee 100644
--- a/engines/made/made.cpp
+++ b/engines/made/made.cpp
@@ -67,9 +67,7 @@ MadeEngine::MadeEngine(OSystem *syst, const MadeGameDescription *gameDesc) : Eng
_console = new MadeConsole(this);
- int cd_num = ConfMan.getInt("cdrom");
- if (cd_num >= 0)
- _system->getAudioCDManager()->openCD(cd_num);
+ _system->getAudioCDManager()->open();
_pmvPlayer = new PmvPlayer(this, _mixer);
_res = new ResourceReader();
@@ -270,7 +268,7 @@ void MadeEngine::handleEvents() {
}
}
- _system->getAudioCDManager()->updateCD();
+ _system->getAudioCDManager()->update();
}
diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp
index 57f0415285..a74bfa245f 100644
--- a/engines/sci/sound/audio.cpp
+++ b/engines/sci/sound/audio.cpp
@@ -28,6 +28,7 @@
#include "backends/audiocd/audiocd.h"
+#include "common/config-manager.h"
#include "common/file.h"
#include "common/memstream.h"
#include "common/system.h"
@@ -44,7 +45,7 @@
namespace Sci {
AudioPlayer::AudioPlayer(ResourceManager *resMan) : _resMan(resMan), _audioRate(11025),
- _syncResource(NULL), _syncOffset(0), _audioCdStart(0) {
+ _syncResource(NULL), _syncOffset(0), _audioCdStart(0), _initCD(false) {
_mixer = g_system->getMixer();
_wPlayFlag = false;
@@ -511,6 +512,12 @@ void AudioPlayer::stopSoundSync() {
}
int AudioPlayer::audioCdPlay(int track, int start, int duration) {
+ if (!_initCD) {
+ // Initialize CD mode if we haven't already
+ g_system->getAudioCDManager()->open();
+ _initCD = true;
+ }
+
if (getSciVersion() == SCI_VERSION_1_1) {
// King's Quest VI CD Audio format
_audioCdStart = g_system->getMillis();
diff --git a/engines/sci/sound/audio.h b/engines/sci/sound/audio.h
index 9e65d6e0c8..4a8b26567d 100644
--- a/engines/sci/sound/audio.h
+++ b/engines/sci/sound/audio.h
@@ -97,6 +97,7 @@ private:
uint _syncOffset;
uint32 _audioCdStart;
bool _wPlayFlag;
+ bool _initCD;
};
} // End of namespace Sci
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 89d2d3dc72..d9148ed300 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1275,10 +1275,7 @@ void ScummEngine::setupScumm() {
// On some systems it's not safe to run CD audio games from the CD.
if (_game.features & GF_AUDIOTRACKS && !Common::File::exists("CDDA.SOU")) {
checkCD();
-
- int cd_num = ConfMan.getInt("cdrom");
- if (cd_num >= 0)
- _system->getAudioCDManager()->openCD(cd_num);
+ _system->getAudioCDManager()->open();
}
// Create the sound manager
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 4d70ee8482..404bdd022c 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -1093,7 +1093,7 @@ int Sound::pollCD() const {
void Sound::updateCD() {
if (!_isLoomSteam)
- g_system->getAudioCDManager()->updateCD();
+ g_system->getAudioCDManager()->update();
}
AudioCDManager::Status Sound::getCDStatus() {
diff --git a/engines/teenagent/teenagent.cpp b/engines/teenagent/teenagent.cpp
index d5a8b8e2dc..4dc785754c 100644
--- a/engines/teenagent/teenagent.cpp
+++ b/engines/teenagent/teenagent.cpp
@@ -545,6 +545,10 @@ Common::Error TeenAgentEngine::run() {
syncSoundSettings();
+ // Initialize CD audio
+ if (_gameDescription->flags & ADGF_CD)
+ g_system->getAudioCDManager()->open();
+
setMusic(1);
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, music, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, false);
diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp
index 2adddca4fd..1b733a08ba 100644
--- a/engines/tinsel/tinsel.cpp
+++ b/engines/tinsel/tinsel.cpp
@@ -841,9 +841,7 @@ TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc)
if (!scumm_stricmp(g->gameid, gameid))
_gameId = g->id;
- int cd_num = ConfMan.getInt("cdrom");
- if (cd_num >= 0)
- _system->getAudioCDManager()->openCD(cd_num);
+ _system->getAudioCDManager()->open();
_mousePos.x = 0;
_mousePos.y = 0;
@@ -975,7 +973,7 @@ Common::Error TinselEngine::run() {
// Check for time to do next game cycle
if ((g_system->getMillis() > timerVal + GAME_FRAME_DELAY)) {
timerVal = g_system->getMillis();
- _system->getAudioCDManager()->updateCD();
+ _system->getAudioCDManager()->update();
NextGameCycle();
}