[Scummvm-cvs-logs] scummvm master -> 5336882555e4834bb5b93fddcf4231d3def9d028

lordhoto lordhoto at gmail.com
Mon Mar 21 01:49:02 CET 2016


This automated email contains information about 37 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
fb6fe1332a BACKENDS: Move to an openCD() without parameters as the public API
016a6000e4 CINE: Ensure openCD() is called
c0e11a431d SCI: Ensure openCD() is called
e9441aa809 TEENAGENT: Ensure openCD() is called
fc8480c845 KYRA: Ensure openCD() is called
ec5df573cb AGOS: Mark the audio CD playback as emulate only
30e68efac4 BACKENDS: Add a closeCD() function to the AudioCDManager
55a87c59b6 BACKENDS: Use a virtual function for creating the SDL audio CD manager
47a82f2d1b BACKENDS: Add a custom Mac OS X CD audio manager
a1a4fc0d51 BACKENDS: Add a Linux CD-ROM audio player
91d695d4a2 BACKENDS: Detect the CD drive from the game's path on Linux
2ee85fcbd9 BACKENDS: Use a threading for buffering Linux CD audio
2aa2a6b56c BACKENDS: Add a Win32 audio CD player
e39faa239a BACKENDS: Use a second thread to assist in buffering Win32 CD audio
1626fbd633 BACKENDS: Allow for specifying a drive via the cdrom option
442f91c622 BACKENDS: Add support for opening a CD on Linux by path or drive
4a6c7b5c83 BACKENDS: Add support for opening a CD on Windows by drive
dc0d4fcf30 BACKENDS: Add support for opening a CD on Mac OS X by path or drive
aa6ff44440 BACKENDS: Only expose one set of functions for AudioCDManager
41a1dcb0bb BACKENDS: Switch to a common base class for threaded audio CD streams
c6c0f527a7 BACKENDS: Switch to CDROMREADRAW for Linux audio CD
90899860b4 BACKENDS: Formatting fixes.
b32f347531 SDL: Get rid of reference to SDL 1.3.
8cd4b881e3 BACKENDS: Add ScummVM license header for AudioCD code.
5340df3faf POSIX: Cleanup AudioCD manager creation.
5e0b136992 BACKENDS: Fill buffer at start of AudioCD playback.
b9b3d4557c WIN32: Fix AudioCD compilation.
075bde796e WIN32: Silence warning in AudioCD code.
045b583141 WIN32: Guard AudioCD code by WIN32 check.
2872c7f9eb WIN32: Fix compilation with MinGW-w64.
88f8ecde0b WIN32: Supply required DDK headers for older MSVC.
6a127df564 WIN32: Cleanup header includes for AudioCD code.
ad678cf083 MACOSX: Replace manual uint parsing by strtol.
f75bc48141 MACOSX: Guard audio cd code by define check.
7725e00e9a LINUX: Guard Linux audio CD code.
afe8ea139c MACOSX: Add comment about track ID search loop.
5336882555 Merge pull request #704 from lordhoto/clone2727-audiocd


Commit: fb6fe1332aaa9ab4d0ec0ad4ee8015ce4076bd85
    https://github.com/scummvm/scummvm/commit/fb6fe1332aaa9ab4d0ec0ad4ee8015ce4076bd85
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:52:24+01:00

Commit Message:
BACKENDS: Move to an openCD() without parameters as the public API

Changed paths:
    backends/audiocd/audiocd.h
    backends/audiocd/default/default-audiocd.cpp
    backends/audiocd/default/default-audiocd.h
    backends/platform/dc/dc.h
    backends/platform/dc/dcmain.cpp
    backends/platform/ds/arm9/source/osystem_ds.cpp
    backends/platform/ds/arm9/source/osystem_ds.h
    engines/drascula/drascula.cpp
    engines/gob/gob.cpp
    engines/groovie/groovie.cpp
    engines/made/made.cpp
    engines/scumm/scumm.cpp
    engines/tinsel/tinsel.cpp



diff --git a/backends/audiocd/audiocd.h b/backends/audiocd/audiocd.h
index 6eae8e0..72a01fd 100644
--- a/backends/audiocd/audiocd.h
+++ b/backends/audiocd/audiocd.h
@@ -109,10 +109,9 @@ public:
 
 	/**
 	 * 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;
+	virtual bool openCD() = 0;
 
 	/**
 	 * Poll CD status.
diff --git a/backends/audiocd/default/default-audiocd.cpp b/backends/audiocd/default/default-audiocd.cpp
index abf80ac..0c5bb8d 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() {
@@ -152,3 +153,7 @@ DefaultAudioCDManager::Status DefaultAudioCDManager::getStatus() const {
 	info.playing = isPlaying();
 	return info;
 }
+
+bool DefaultAudioCDManager::openCD() {
+	return openCD(ConfMan.getInt("cdrom"));
+}
diff --git a/backends/audiocd/default/default-audiocd.h b/backends/audiocd/default/default-audiocd.h
index 9e4ba6b..1d64690 100644
--- a/backends/audiocd/default/default-audiocd.h
+++ b/backends/audiocd/default/default-audiocd.h
@@ -42,6 +42,13 @@ public:
 	void update();
 	virtual Status getStatus() const; // Subclasses should override for better status results
 
+	bool openCD();
+
+	/**
+	 * 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; }
diff --git a/backends/platform/dc/dc.h b/backends/platform/dc/dc.h
index d8ab549..80e484f 100644
--- a/backends/platform/dc/dc.h
+++ b/backends/platform/dc/dc.h
@@ -58,7 +58,7 @@ class DCHardware {
 
 class DCCDManager : public DefaultAudioCDManager {
   // Initialize the specified CD drive for audio playback.
-  bool openCD(int drive);
+  bool openCD();
 
   // Poll cdrom status
   // Returns true if cd audio is playing
diff --git a/backends/platform/dc/dcmain.cpp b/backends/platform/dc/dcmain.cpp
index eede796..aa8430a 100644
--- a/backends/platform/dc/dcmain.cpp
+++ b/backends/platform/dc/dcmain.cpp
@@ -123,7 +123,7 @@ void DCCDManager::updateCD()
   // Dummy.  The CD drive takes care of itself.
 }
 
-bool DCCDManager::openCD(int drive)
+bool DCCDManager::openCD()
 {
   // Dummy.
   return true;
diff --git a/backends/platform/ds/arm9/source/osystem_ds.cpp b/backends/platform/ds/arm9/source/osystem_ds.cpp
index c53f575..f23192c 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 f4dbac6..9b66e24 100644
--- a/backends/platform/ds/arm9/source/osystem_ds.h
+++ b/backends/platform/ds/arm9/source/osystem_ds.h
@@ -130,7 +130,7 @@ 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 bool pollCD();
 	virtual void playCD(int track, int num_loops, int start_frame, int duration);
 	virtual void stopCD();
diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp
index c72d77c..1a7659f 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()->openCD();
 
 	_lang = kEnglish;
 
diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp
index 24bdb85..df2d804 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()->openCD();
 
 	_global->_debugFlag = 1;
 	_video->_doRangeClamp = true;
diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp
index 2021cef..a25bf00 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()->openCD();
 	}
 
 	while (!shouldQuit()) {
diff --git a/engines/made/made.cpp b/engines/made/made.cpp
index ab07ef7..57130e2 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()->openCD();
 
 	_pmvPlayer = new PmvPlayer(this, _mixer);
 	_res = new ResourceReader();
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 89d2d3d..680bdb2 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()->openCD();
 	}
 
 	// Create the sound manager
diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp
index 2adddca..782dd7a 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()->openCD();
 
 	_mousePos.x = 0;
 	_mousePos.y = 0;


Commit: 016a6000e425bf4b5b696eae93316afbba15eb79
    https://github.com/scummvm/scummvm/commit/016a6000e425bf4b5b696eae93316afbba15eb79
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:52:35+01:00

Commit Message:
CINE: Ensure openCD() is called

Changed paths:
    engines/cine/sound.cpp



diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp
index 7cab067..0e7da2e 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()->openCD();
 }
 
 PCSound::~PCSound() {


Commit: c0e11a431dd5c4a82ab754c3f94bb95ee46de792
    https://github.com/scummvm/scummvm/commit/c0e11a431dd5c4a82ab754c3f94bb95ee46de792
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:53:08+01:00

Commit Message:
SCI: Ensure openCD() is called

Changed paths:
    engines/sci/sound/audio.cpp
    engines/sci/sound/audio.h



diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp
index 57f0415..5e5e8b0 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()->openCD();
+		_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 9e65d6e..4a8b265 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


Commit: e9441aa809634900ad4cbd414510f057f62329f2
    https://github.com/scummvm/scummvm/commit/e9441aa809634900ad4cbd414510f057f62329f2
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:53:14+01:00

Commit Message:
TEENAGENT: Ensure openCD() is called

Changed paths:
    engines/teenagent/teenagent.cpp



diff --git a/engines/teenagent/teenagent.cpp b/engines/teenagent/teenagent.cpp
index d5a8b8e..f5d9f72 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()->openCD();
+
 	setMusic(1);
 	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, music, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, false);
 


Commit: fc8480c84582d83556fc70f8a8fed004bbe9f766
    https://github.com/scummvm/scummvm/commit/fc8480c84582d83556fc70f8a8fed004bbe9f766
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:53:20+01:00

Commit Message:
KYRA: Ensure openCD() is called

Changed paths:
    engines/kyra/sound_towns.cpp



diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp
index 65ab4f3..b269cb4 100644
--- a/engines/kyra/sound_towns.cpp
+++ b/engines/kyra/sound_towns.cpp
@@ -68,6 +68,9 @@ bool SoundTowns::init() {
 	_player->driver()->intf()->callback(70, 0x33);*/
 	_player->driver()->setOutputVolume(1, 118, 118);
 
+	// Initialize CD for audio
+	g_system->getAudioCDManager()->openCD();
+
 	return true;
 }
 
@@ -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()->openCD();
+
 	return reslt;
 }
 
@@ -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()->openCD();
+
 		// 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


Commit: ec5df573cbd8ea33e0ee34f27cc7d732485f3ed1
    https://github.com/scummvm/scummvm/commit/ec5df573cbd8ea33e0ee34f27cc7d732485f3ed1
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:53:26+01:00

Commit Message:
AGOS: Mark the audio CD playback as emulate only

Changed paths:
    engines/agos/res_snd.cpp



diff --git a/engines/agos/res_snd.cpp b/engines/agos/res_snd.cpp
index d04f173..3a092e6 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;
 


Commit: 30e68efac41a96e97fc9d42d0b7f9903e76ccfb5
    https://github.com/scummvm/scummvm/commit/30e68efac41a96e97fc9d42d0b7f9903e76ccfb5
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:53:35+01:00

Commit Message:
BACKENDS: Add a closeCD() function to the AudioCDManager

Changed paths:
    backends/audiocd/audiocd.h
    backends/audiocd/default/default-audiocd.h
    backends/audiocd/sdl/sdl-audiocd.cpp
    backends/audiocd/sdl/sdl-audiocd.h
    backends/platform/dc/dc.h
    backends/platform/ds/arm9/source/osystem_ds.h



diff --git a/backends/audiocd/audiocd.h b/backends/audiocd/audiocd.h
index 72a01fd..598893e 100644
--- a/backends/audiocd/audiocd.h
+++ b/backends/audiocd/audiocd.h
@@ -114,6 +114,11 @@ public:
 	virtual bool openCD() = 0;
 
 	/**
+	 * Close the currently open CD drive
+	 */
+	virtual void closeCD() = 0;
+
+	/**
 	 * Poll CD status.
 	 * @return true if CD audio is playing
 	 */
diff --git a/backends/audiocd/default/default-audiocd.h b/backends/audiocd/default/default-audiocd.h
index 1d64690..6011a8f 100644
--- a/backends/audiocd/default/default-audiocd.h
+++ b/backends/audiocd/default/default-audiocd.h
@@ -43,6 +43,7 @@ public:
 	virtual Status getStatus() const; // Subclasses should override for better status results
 
 	bool openCD();
+	virtual void closeCD() {}
 
 	/**
 	 * Open a CD using the specified drive index
diff --git a/backends/audiocd/sdl/sdl-audiocd.cpp b/backends/audiocd/sdl/sdl-audiocd.cpp
index ff50c56..d745f29 100644
--- a/backends/audiocd/sdl/sdl-audiocd.cpp
+++ b/backends/audiocd/sdl/sdl-audiocd.cpp
@@ -43,10 +43,7 @@ SdlAudioCDManager::SdlAudioCDManager()
 }
 
 SdlAudioCDManager::~SdlAudioCDManager() {
-	if (_cdrom) {
-		SDL_CDStop(_cdrom);
-		SDL_CDClose(_cdrom);
-	}
+	closeCD();
 }
 
 bool SdlAudioCDManager::openCD(int drive) {
@@ -67,6 +64,14 @@ bool SdlAudioCDManager::openCD(int drive) {
 	return (_cdrom != NULL);
 }
 
+void SdlAudioCDManager::closeCD() {
+	if (_cdrom) {
+                SDL_CDStop(_cdrom);
+                SDL_CDClose(_cdrom);
+		_cdrom = 0;
+        }
+}
+
 void SdlAudioCDManager::stopCD() {
 	// Stop CD Audio in 1/10th of a second
 	_cdStopTime = SDL_GetTicks() + 100;
diff --git a/backends/audiocd/sdl/sdl-audiocd.h b/backends/audiocd/sdl/sdl-audiocd.h
index bfad7b6..4ece8dd 100644
--- a/backends/audiocd/sdl/sdl-audiocd.h
+++ b/backends/audiocd/sdl/sdl-audiocd.h
@@ -39,6 +39,7 @@ public:
 
 protected:
 	virtual bool openCD(int drive);
+	virtual void closeCD();
 	virtual void updateCD();
 	virtual bool pollCD() const;
 	virtual void playCD(int track, int num_loops, int start_frame, int duration);
diff --git a/backends/platform/dc/dc.h b/backends/platform/dc/dc.h
index 80e484f..b490803 100644
--- a/backends/platform/dc/dc.h
+++ b/backends/platform/dc/dc.h
@@ -60,6 +60,9 @@ class DCCDManager : public DefaultAudioCDManager {
   // Initialize the specified CD drive for audio playback.
   bool openCD();
 
+	// Close the open CD drive
+	void closeCD() {}
+
   // Poll cdrom status
   // Returns true if cd audio is playing
   bool pollCD();
diff --git a/backends/platform/ds/arm9/source/osystem_ds.h b/backends/platform/ds/arm9/source/osystem_ds.h
index 9b66e24..9f73e12 100644
--- a/backends/platform/ds/arm9/source/osystem_ds.h
+++ b/backends/platform/ds/arm9/source/osystem_ds.h
@@ -131,6 +131,7 @@ public:
 	// and should be replaced by an AudioCDManager subclass,
 	// see backends/audiocd/ and common/system.h
 	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();


Commit: 55a87c59b61fe6758faee3a3e388b2a8979c7cb8
    https://github.com/scummvm/scummvm/commit/55a87c59b61fe6758faee3a3e388b2a8979c7cb8
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:53:40+01:00

Commit Message:
BACKENDS: Use a virtual function for creating the SDL audio CD manager

Changed paths:
    backends/platform/sdl/sdl.cpp
    backends/platform/sdl/sdl.h



diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index fffb9d5..a110a9f 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 1.3
+#if SDL_VERSION_ATLEAST(1, 3, 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 5ee56d0..c93c830 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;


Commit: 47a82f2d1b776ec46d98715c9af3e7ec37a3d206
    https://github.com/scummvm/scummvm/commit/47a82f2d1b776ec46d98715c9af3e7ec37a3d206
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:53:55+01:00

Commit Message:
BACKENDS: Add a custom Mac OS X CD audio manager

Since Mac OS X Carbon/Cocoa API isn't stable (in that it's changed multiple times over the years). Maintaining two versions of the same code (one in some foreign language with overly long names) isn't very appealing to me.

Changed paths:
  A backends/audiocd/macosx/macosx-audiocd.cpp
  A backends/audiocd/macosx/macosx-audiocd.h
    backends/module.mk
    backends/platform/sdl/macosx/macosx.cpp
    backends/platform/sdl/macosx/macosx.h



diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp
new file mode 100644
index 0000000..a8ba712
--- /dev/null
+++ b/backends/audiocd/macosx/macosx-audiocd.cpp
@@ -0,0 +1,239 @@
+/* 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 <sys/stat.h>
+#include <sys/mount.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();
+
+	void playCD(int track, int num_loops, int start_frame, int duration);
+	void closeCD();
+
+protected:
+	bool openCD(int 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() {
+	closeCD();
+}
+
+/**
+ * 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) {
+	closeCD();
+
+	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);
+}
+
+void MacOSXAudioCDManager::closeCD() {
+	stop();
+	_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;
+}
+
+void MacOSXAudioCDManager::playCD(int track, int numLoops, int startFrame, int duration) {
+	if (!_trackMap.contains(track) || (!numLoops && !startFrame))
+		return;
+
+	// 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;
+	}
+
+	Audio::AudioStream *audioStream = Audio::makeAIFFStream(stream, DisposeAfterUse::YES);
+	if (!audioStream) {
+		warning("Track '%s' is not an AIFF track", fileName.c_str());
+		return;
+	}
+
+	Audio::SeekableAudioStream *seekStream = dynamic_cast<Audio::SeekableAudioStream *>(audioStream);
+	if (!seekStream) {
+		warning("Track '%s' is not seekable", fileName.c_str());
+		return;
+	}
+
+	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);
+}
+
+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 trackID = 0, j = 0;
+
+				for (; j < fileName.size() && !Common::isDigit(fileName[j]); j++)
+					;
+
+				for (; j < fileName.size() && Common::isDigit(fileName[j]); j++)
+					trackID = trackID * 10 + (fileName[j] - '0');
+
+				_trackMap[trackID - 1] = drivePath + '/' + fileName;
+			}
+		}
+	}
+
+	return true;
+}
+
+AudioCDManager *createMacOSXAudioCDManager() {
+	return new MacOSXAudioCDManager();
+}
diff --git a/backends/audiocd/macosx/macosx-audiocd.h b/backends/audiocd/macosx/macosx-audiocd.h
new file mode 100644
index 0000000..cfa979e
--- /dev/null
+++ b/backends/audiocd/macosx/macosx-audiocd.h
@@ -0,0 +1,39 @@
+/* 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/module.mk b/backends/module.mk
index 3d412c0..01e601e 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -97,6 +97,7 @@ endif
 
 ifdef MACOSX
 MODULE_OBJS += \
+	audiocd/macosx/macosx-audiocd.o \
 	midi/coreaudio.o \
 	midi/coremidi.o \
 	updates/macosx/macosx-updates.o \
diff --git a/backends/platform/sdl/macosx/macosx.cpp b/backends/platform/sdl/macosx/macosx.cpp
index 38a2d74..7652c0d 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 c8b4bea..6905284 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


Commit: a1a4fc0d5186ae4aeeb1e5ea99be4b36bf645179
    https://github.com/scummvm/scummvm/commit/a1a4fc0d5186ae4aeeb1e5ea99be4b36bf645179
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:56:28+01:00

Commit Message:
BACKENDS: Add a Linux CD-ROM audio player

Changed paths:
  A backends/audiocd/linux/linux-audiocd.cpp
  A backends/audiocd/linux/linux-audiocd.h
    backends/module.mk
    backends/platform/sdl/posix/posix.cpp
    backends/platform/sdl/posix/posix.h
    configure



diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp
new file mode 100644
index 0000000..a173a9d
--- /dev/null
+++ b/backends/audiocd/linux/linux-audiocd.cpp
@@ -0,0 +1,350 @@
+/* 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 <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/cdrom.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include "backends/audiocd/linux/linux-audiocd.h"
+
+#include "audio/audiostream.h"
+#include "backends/audiocd/default/default-audiocd.h"
+#include "common/array.h"
+#include "common/str.h"
+#include "common/debug.h"
+
+enum {
+	kLeadoutTrack = 0xAA
+};
+
+enum {
+	kBytesPerFrame = 2352,
+	kSamplesPerFrame = kBytesPerFrame / 2
+};
+
+enum {
+	kSecondsPerMinute = 60,
+	kFramesPerSecond = 75
+};
+
+static int getFrameCount(const cdrom_msf0 &msf) {
+	int time = msf.minute;
+	time *= kSecondsPerMinute;
+	time += msf.second;
+	time *= kFramesPerSecond;
+	time += msf.frame;
+	return time;
+}
+
+class LinuxAudioCDStream : public Audio::SeekableAudioStream {
+public:
+	LinuxAudioCDStream(int fd, const cdrom_tocentry &startEntry, const cdrom_tocentry &endEntry);
+
+	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;
+
+private:
+	int _fd;
+	const cdrom_tocentry &_startEntry, &_endEntry;
+	int16 _buffer[kSamplesPerFrame];
+	int _frame;
+	uint _bufferPos;
+};
+
+LinuxAudioCDStream::LinuxAudioCDStream(int fd, const cdrom_tocentry &startEntry, const cdrom_tocentry &endEntry) :
+		_fd(fd), _startEntry(startEntry), _endEntry(endEntry), _buffer(), _frame(0), _bufferPos(kSamplesPerFrame) {
+}
+
+int LinuxAudioCDStream::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()) {
+		// Figure out the MSF of the frame we're looking for
+		int frame = _frame + getFrameCount(_startEntry.cdte_addr.msf);
+
+		int seconds = frame / kFramesPerSecond;
+		frame %= kFramesPerSecond;
+		int minutes = seconds / kSecondsPerMinute;
+		seconds %= kSecondsPerMinute;
+
+		// Request to read that frame
+		cdrom_read_audio readAudio;
+		readAudio.addr.msf.minute = minutes;
+		readAudio.addr.msf.second = seconds;
+		readAudio.addr.msf.frame = frame;
+		readAudio.addr_format = CDROM_MSF;
+		readAudio.nframes = 1;
+		readAudio.buf = reinterpret_cast<__u8*>(_buffer);
+
+		if (ioctl(_fd, CDROMREADAUDIO, &readAudio) < 0) {
+			warning("Failed to read audio");
+			_frame = getFrameCount(_endEntry.cdte_addr.msf);
+			return samples;
+		}
+
+		_frame++;
+
+		// Copy the samples over
+		for (_bufferPos = 0; _bufferPos < kSamplesPerFrame && samples < numSamples; )
+			buffer[samples++] = _buffer[_bufferPos++];
+	}
+
+	return samples;
+}
+
+bool LinuxAudioCDStream::endOfData() const {
+	return getFrameCount(_startEntry.cdte_addr.msf) + _frame >= getFrameCount(_endEntry.cdte_addr.msf) && _bufferPos == kSamplesPerFrame;
+}
+
+bool LinuxAudioCDStream::seek(const Audio::Timestamp &where) {
+	// Convert to the frame number
+	// Really not much else needed
+	_bufferPos = kSamplesPerFrame;
+	_frame = where.convertToFramerate(kFramesPerSecond).totalNumberOfFrames();
+	return true;
+}
+
+Audio::Timestamp LinuxAudioCDStream::getLength() const {
+	return Audio::Timestamp(0, getFrameCount(_endEntry.cdte_addr.msf) - getFrameCount(_startEntry.cdte_addr.msf), 75);
+}
+
+class LinuxAudioCDManager : public DefaultAudioCDManager {
+public:
+	LinuxAudioCDManager();
+	~LinuxAudioCDManager();
+
+	bool openCD(int drive);
+	void closeCD();
+	void playCD(int track, int numLoops, int startFrame, int duration);
+
+private:
+	struct Device {
+		Device(const Common::String &n, dev_t m) : name(n), mode(m) {}
+		Common::String name;
+		dev_t mode;
+	};
+
+	Common::Array<Device> scanDevices();
+	bool tryAddDrive(const Common::String &drive, Common::Array<Device> &devices);
+	bool loadTOC();
+
+	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() {
+	closeCD();
+}
+
+bool LinuxAudioCDManager::openCD(int drive) {
+	closeCD();
+
+	Common::Array<Device> devices = scanDevices();
+	if (devices.empty())
+		return false;
+
+	_fd = open(devices[0].name.c_str(), O_RDONLY | O_NONBLOCK, 0);
+	if (_fd < 0)
+		return false;
+
+	if (!loadTOC()) {
+		closeCD();
+		return false;
+	}
+
+	return true;
+}
+
+void LinuxAudioCDManager::closeCD() {
+	if (_fd < 0)
+		return;
+
+	stop();
+	close(_fd);
+	memset(&_tocHeader, 0, sizeof(_tocHeader));
+	_tocEntries.clear();
+}
+
+void LinuxAudioCDManager::playCD(int track, int numLoops, int startFrame, int duration) {
+	// Stop any previous track
+	stop();
+
+	// 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;
+	}
+
+	// Bail if the track isn't an audio track
+	if ((_tocEntries[track].cdte_ctrl & 0x04) != 0) {
+		warning("Track %d is not audio", track);
+		return;
+	}
+
+	// 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);
+}
+
+Common::Array<LinuxAudioCDManager::Device> LinuxAudioCDManager::scanDevices() {
+	Common::Array<Device> devices;
+	tryAddDrive("/dev/cdrom", devices);
+	return devices;
+}
+
+bool LinuxAudioCDManager::tryAddDrive(const Common::String &drive, Common::Array<Device> &devices) {
+	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;
+
+	// 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, stbuf.st_rdev));
+			return true;
+		}
+	}
+
+	return false;
+}
+
+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;
+}
+
+AudioCDManager *createLinuxAudioCDManager() {
+	return new LinuxAudioCDManager();
+}
diff --git a/backends/audiocd/linux/linux-audiocd.h b/backends/audiocd/linux/linux-audiocd.h
new file mode 100644
index 0000000..860afff
--- /dev/null
+++ b/backends/audiocd/linux/linux-audiocd.h
@@ -0,0 +1,38 @@
+/* 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
+
+#ifdef USE_LINUXCD
+
+class AudioCDManager;
+
+/**
+ * Create an audio CD manager using the Linux CDROM API
+ */
+AudioCDManager *createLinuxAudioCDManager();
+
+#endif
+
+#endif
+
diff --git a/backends/module.mk b/backends/module.mk
index 01e601e..40b63e2 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -129,6 +129,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/sdl/posix/posix.cpp b/backends/platform/sdl/posix/posix.cpp
index 525c74a..3989bd0 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>
@@ -238,5 +243,12 @@ bool OSystem_POSIX::displayLogFile() {
 	return WIFEXITED(status) && WEXITSTATUS(status) == 0;
 }
 
+#ifdef USE_LINUXCD
+
+AudioCDManager *OSystem_POSIX::createAudioCDManager() {
+	return createLinuxAudioCDManager();
+}
+
+#endif
 
 #endif
diff --git a/backends/platform/sdl/posix/posix.h b/backends/platform/sdl/posix/posix.h
index f67515d..9f69bd2 100644
--- a/backends/platform/sdl/posix/posix.h
+++ b/backends/platform/sdl/posix/posix.h
@@ -59,6 +59,11 @@ protected:
 	virtual Common::String getDefaultConfigFileName();
 
 	virtual Common::WriteStream *createLogFile();
+
+#ifdef USE_LINUXCD
+	// Override createAudioCDManager() for Linux
+	virtual AudioCDManager *createAudioCDManager();
+#endif
 };
 
 #endif
diff --git a/configure b/configure
index 0e7a5a9..c958e9a 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


Commit: 91d695d4a2fea6742c31abd246a22c40797b014f
    https://github.com/scummvm/scummvm/commit/91d695d4a2fea6742c31abd246a22c40797b014f
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:56:39+01:00

Commit Message:
BACKENDS: Detect the CD drive from the game's path on Linux

Changed paths:
    backends/audiocd/linux/linux-audiocd.cpp



diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp
index a173a9d..c079c2d 100644
--- a/backends/audiocd/linux/linux-audiocd.cpp
+++ b/backends/audiocd/linux/linux-audiocd.cpp
@@ -32,6 +32,7 @@
 #include "audio/audiostream.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"
 
@@ -153,14 +154,20 @@ public:
 
 private:
 	struct Device {
-		Device(const Common::String &n, dev_t m) : name(n), mode(m) {}
+		Device(const Common::String &n, dev_t d) : name(n), device(d) {}
 		Common::String name;
-		dev_t mode;
+		dev_t device;
 	};
 
-	Common::Array<Device> scanDevices();
-	bool tryAddDrive(const Common::String &drive, Common::Array<Device> &devices);
+	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;
@@ -193,11 +200,11 @@ LinuxAudioCDManager::~LinuxAudioCDManager() {
 bool LinuxAudioCDManager::openCD(int drive) {
 	closeCD();
 
-	Common::Array<Device> devices = scanDevices();
-	if (devices.empty())
+	DeviceList devices = scanDevices();
+	if (drive >= (int)devices.size())
 		return false;
 
-	_fd = open(devices[0].name.c_str(), O_RDONLY | O_NONBLOCK, 0);
+	_fd = open(devices[drive].name.c_str(), O_RDONLY | O_NONBLOCK, 0);
 	if (_fd < 0)
 		return false;
 
@@ -259,13 +266,21 @@ void LinuxAudioCDManager::playCD(int track, int numLoops, int startFrame, int du
 		true);
 }
 
-Common::Array<LinuxAudioCDManager::Device> LinuxAudioCDManager::scanDevices() {
-	Common::Array<Device> devices;
-	tryAddDrive("/dev/cdrom", devices);
+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(const Common::String &drive, Common::Array<Device> &devices) {
+bool LinuxAudioCDManager::tryAddDrive(DeviceList &devices, const Common::String &drive) {
 	struct stat stbuf;
 	if (stat(drive.c_str(), &stbuf) < 0)
 		return false;
@@ -274,6 +289,13 @@ bool LinuxAudioCDManager::tryAddDrive(const Common::String &drive, Common::Array
 	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) {
@@ -283,7 +305,7 @@ bool LinuxAudioCDManager::tryAddDrive(const Common::String &drive, Common::Array
 		bool isCD = ioctl(fd, CDROMSUBCHNL, &info) == 0 || isTrayEmpty(errno);
 		close(fd);
 		if (isCD) {
-			devices.push_back(Device(drive, stbuf.st_rdev));
+			devices.push_back(Device(drive, device));
 			return true;
 		}
 	}
@@ -291,6 +313,29 @@ bool LinuxAudioCDManager::tryAddDrive(const Common::String &drive, Common::Array
 	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;
@@ -345,6 +390,14 @@ bool LinuxAudioCDManager::loadTOC() {
 	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();
 }


Commit: 2ee85fcbd9a457d041436e4cdb600a2171ad0b1c
    https://github.com/scummvm/scummvm/commit/2ee85fcbd9a457d041436e4cdb600a2171ad0b1c
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:56:46+01:00

Commit Message:
BACKENDS: Use a threading for buffering Linux CD audio

Changed paths:
    backends/audiocd/linux/linux-audiocd.cpp



diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp
index c079c2d..917546c 100644
--- a/backends/audiocd/linux/linux-audiocd.cpp
+++ b/backends/audiocd/linux/linux-audiocd.cpp
@@ -33,8 +33,11 @@
 #include "backends/audiocd/default/default-audiocd.h"
 #include "common/array.h"
 #include "common/config-manager.h"
+#include "common/mutex.h"
+#include "common/queue.h"
 #include "common/str.h"
 #include "common/debug.h"
+#include "common/timer.h"
 
 enum {
 	kLeadoutTrack = 0xAA
@@ -50,6 +53,11 @@ enum {
 	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;
@@ -62,6 +70,7 @@ static int getFrameCount(const cdrom_msf0 &msf) {
 class LinuxAudioCDStream : public Audio::SeekableAudioStream {
 public:
 	LinuxAudioCDStream(int fd, const cdrom_tocentry &startEntry, const cdrom_tocentry &endEntry);
+	~LinuxAudioCDStream();
 
 	int readBuffer(int16 *buffer, const int numSamples);
 	bool isStereo() const { return true; }
@@ -76,10 +85,27 @@ private:
 	int16 _buffer[kSamplesPerFrame];
 	int _frame;
 	uint _bufferPos;
+
+	Common::Queue<int16 *> _bufferQueue;
+	int _bufferFrame;
+	Common::Mutex _mutex;
+
+	bool readNextFrame();
+	static void timerProc(void *refCon);
+	void onTimer();
+	void emptyQueue();
+	void startTimer();
+	void stopTimer();
 };
 
 LinuxAudioCDStream::LinuxAudioCDStream(int fd, const cdrom_tocentry &startEntry, const cdrom_tocentry &endEntry) :
-		_fd(fd), _startEntry(startEntry), _endEntry(endEntry), _buffer(), _frame(0), _bufferPos(kSamplesPerFrame) {
+		_fd(fd), _startEntry(startEntry), _endEntry(endEntry), _buffer(), _frame(0), _bufferPos(kSamplesPerFrame), _bufferFrame(0) {
+	startTimer();
+}
+
+LinuxAudioCDStream::~LinuxAudioCDStream() {
+	stopTimer();
+	emptyQueue();
 }
 
 int LinuxAudioCDStream::readBuffer(int16 *buffer, const int numSamples) {
@@ -94,8 +120,90 @@ int LinuxAudioCDStream::readBuffer(int16 *buffer, const int numSamples) {
 		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 LinuxAudioCDStream::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 LinuxAudioCDStream::endOfData() const {
+	return getFrameCount(_startEntry.cdte_addr.msf) + _frame >= getFrameCount(_endEntry.cdte_addr.msf) && _bufferPos == kSamplesPerFrame;
+}
+
+bool LinuxAudioCDStream::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 LinuxAudioCDStream::getLength() const {
+	return Audio::Timestamp(0, getFrameCount(_endEntry.cdte_addr.msf) - getFrameCount(_startEntry.cdte_addr.msf), 75);
+}
+
+void LinuxAudioCDStream::timerProc(void *refCon) {
+	static_cast<LinuxAudioCDStream *>(refCon)->onTimer();
+}
+
+void LinuxAudioCDStream::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 (getFrameCount(_startEntry.cdte_addr.msf) + _bufferFrame >= getFrameCount(_endEntry.cdte_addr.msf))
+		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 (queueCount < kBufferThreshold && getFrameCount(_startEntry.cdte_addr.msf) + _bufferFrame < getFrameCount(_endEntry.cdte_addr.msf)) {
+		int16 *buffer = new int16[kSamplesPerFrame];
+
 		// Figure out the MSF of the frame we're looking for
-		int frame = _frame + getFrameCount(_startEntry.cdte_addr.msf);
+		int frame = _bufferFrame + getFrameCount(_startEntry.cdte_addr.msf);
 
 		int seconds = frame / kFramesPerSecond;
 		frame %= kFramesPerSecond;
@@ -109,38 +217,34 @@ int LinuxAudioCDStream::readBuffer(int16 *buffer, const int numSamples) {
 		readAudio.addr.msf.frame = frame;
 		readAudio.addr_format = CDROM_MSF;
 		readAudio.nframes = 1;
-		readAudio.buf = reinterpret_cast<__u8*>(_buffer);
+		readAudio.buf = reinterpret_cast<__u8*>(buffer);
 
 		if (ioctl(_fd, CDROMREADAUDIO, &readAudio) < 0) {
 			warning("Failed to read audio");
-			_frame = getFrameCount(_endEntry.cdte_addr.msf);
-			return samples;
+			_bufferFrame = getFrameCount(_endEntry.cdte_addr.msf);
+			return;
 		}
 
-		_frame++;
+		_bufferFrame++;
 
-		// Copy the samples over
-		for (_bufferPos = 0; _bufferPos < kSamplesPerFrame && samples < numSamples; )
-			buffer[samples++] = _buffer[_bufferPos++];
+		// Now push the buffer onto the queue
+		Common::StackLock lock(_mutex);
+		_bufferQueue.push(buffer);
+		queueCount = _bufferQueue.size();
 	}
-
-	return samples;
 }
 
-bool LinuxAudioCDStream::endOfData() const {
-	return getFrameCount(_startEntry.cdte_addr.msf) + _frame >= getFrameCount(_endEntry.cdte_addr.msf) && _bufferPos == kSamplesPerFrame;
+void LinuxAudioCDStream::startTimer() {
+	g_system->getTimerManager()->installTimerProc(timerProc, 10 * 1000, this, "LinuxAudioCDStream");
 }
 
-bool LinuxAudioCDStream::seek(const Audio::Timestamp &where) {
-	// Convert to the frame number
-	// Really not much else needed
-	_bufferPos = kSamplesPerFrame;
-	_frame = where.convertToFramerate(kFramesPerSecond).totalNumberOfFrames();
-	return true;
+void LinuxAudioCDStream::stopTimer() {
+	g_system->getTimerManager()->removeTimerProc(timerProc);
 }
 
-Audio::Timestamp LinuxAudioCDStream::getLength() const {
-	return Audio::Timestamp(0, getFrameCount(_endEntry.cdte_addr.msf) - getFrameCount(_startEntry.cdte_addr.msf), 75);
+void LinuxAudioCDStream::emptyQueue() {
+	while (!_bufferQueue.empty())
+		delete[] _bufferQueue.pop();
 }
 
 class LinuxAudioCDManager : public DefaultAudioCDManager {


Commit: 2aa2a6b56cd011673e0899a48daac5593e4f198f
    https://github.com/scummvm/scummvm/commit/2aa2a6b56cd011673e0899a48daac5593e4f198f
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:56:51+01:00

Commit Message:
BACKENDS: Add a Win32 audio CD player

Changed paths:
  A backends/audiocd/win32/win32-audiocd.cpp
  A backends/audiocd/win32/win32-audiocd.h
    backends/module.mk
    backends/platform/sdl/win32/win32.cpp
    backends/platform/sdl/win32/win32.h



diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp
new file mode 100644
index 0000000..fdde61c
--- /dev/null
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -0,0 +1,363 @@
+/* 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/win32/win32-audiocd.h"
+
+#include "audio/audiostream.h"
+#include "backends/audiocd/default/default-audiocd.h"
+#include "common/array.h"
+#include "common/config-manager.h"
+#include "common/endian.h"
+#include "common/str.h"
+#include "common/debug.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#ifdef _MSC_VER
+#include <winioctl.h>
+#include <ntddcdrm.h>
+#else
+#include <ddk/ntddcdrm.h>
+#endif
+
+enum {
+	kLeadoutTrack = 0xAA
+};
+
+enum {
+	kBytesPerFrame = 2352,
+	kSamplesPerFrame = kBytesPerFrame / 2
+};
+
+enum {
+	kSecondsPerMinute = 60,
+	kFramesPerSecond = 75
+};
+
+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;
+}
+
+class Win32AudioCDStream : public Audio::SeekableAudioStream {
+public:
+	Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry);
+
+	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;
+
+private:
+	HANDLE _driveHandle;
+	const TRACK_DATA &_startEntry, &_endEntry;
+	int16 _buffer[kSamplesPerFrame];
+	int _frame;
+	uint _bufferPos;
+
+	bool readNextFrame();
+};
+
+Win32AudioCDStream::Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry) :
+		_driveHandle(handle), _startEntry(startEntry), _endEntry(endEntry), _buffer(), _frame(0), _bufferPos(kSamplesPerFrame) {
+	// Read the first frame here. This should hopefully keep games more
+	// in sync instead of potentially waiting in the mixer thread for the
+	// disc to seek to the first frame.
+	readNextFrame();
+}
+
+int Win32AudioCDStream::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 Win32AudioCDStream::readNextFrame() {
+	// Figure out the MSF of the frame we're looking for
+	int frame = _frame + getFrameCount(_startEntry);
+
+	// Request to read that frame. Subtract the pre-gap frames
+	// so that we get to the right sector.
+	RAW_READ_INFO readAudio;
+	memset(&readAudio, 0, sizeof(readAudio));
+	readAudio.DiskOffset.QuadPart = (frame - kPreGapFrames) * 2048;
+	readAudio.SectorCount = 1;
+	readAudio.TrackMode = CDDA;
+	
+	DWORD bytesReturned;
+	bool result = DeviceIoControl(
+		_driveHandle,
+		IOCTL_CDROM_RAW_READ,
+		&readAudio,
+		sizeof(readAudio),
+		&_buffer,
+		sizeof(_buffer),
+		&bytesReturned,
+		NULL);
+	if (!result) {
+		warning("Failed to retrieve CD sector %d: %d", frame, (int)GetLastError());
+		_frame = getFrameCount(_endEntry);
+		return false;
+	}
+
+	_frame++;
+	return true;
+}
+
+bool Win32AudioCDStream::endOfData() const {
+	return getFrameCount(_startEntry) + _frame >= getFrameCount(_endEntry) && _bufferPos == kSamplesPerFrame;
+}
+
+bool Win32AudioCDStream::seek(const Audio::Timestamp &where) {
+	// Convert to the frame number
+	// Really not much else needed
+	_bufferPos = kSamplesPerFrame;
+	_frame = where.convertToFramerate(kFramesPerSecond).totalNumberOfFrames();
+	return true;
+}
+
+Audio::Timestamp Win32AudioCDStream::getLength() const {
+	return Audio::Timestamp(0, getFrameCount(_endEntry) - getFrameCount(_startEntry), 75);
+}
+
+class Win32AudioCDManager : public DefaultAudioCDManager {
+public:
+	Win32AudioCDManager();
+	~Win32AudioCDManager();
+
+	bool openCD(int drive);
+	void closeCD();
+	void playCD(int track, int numLoops, int startFrame, int duration);
+
+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() {
+	closeCD();
+}
+
+bool Win32AudioCDManager::openCD(int drive) {
+	closeCD();
+
+	// 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()) {
+		closeCD();
+		return false;
+	}
+	
+	return false;
+}
+
+void Win32AudioCDManager::closeCD() {
+	// Stop any previous track
+	stop();
+
+	if (_driveHandle != INVALID_HANDLE_VALUE) {
+		CloseHandle(_driveHandle);
+		_driveHandle = INVALID_HANDLE_VALUE;
+	}
+
+	_firstTrack = _lastTrack = 0;
+	_tocEntries.clear();
+}
+
+void Win32AudioCDManager::playCD(int track, int numLoops, int startFrame, int duration) {
+	// Stop any previous track
+	stop();
+
+	// 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;
+	}
+
+	// Bail if the track isn't an audio track
+	if ((_tocEntries[track].Control & 0x04) != 0) {
+		warning("Track %d is not audio", track);
+		return;
+	}
+
+	// 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);
+}
+
+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();
+}
diff --git a/backends/audiocd/win32/win32-audiocd.h b/backends/audiocd/win32/win32-audiocd.h
new file mode 100644
index 0000000..d2a5b25
--- /dev/null
+++ b/backends/audiocd/win32/win32-audiocd.h
@@ -0,0 +1,38 @@
+/* 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 40b63e2..8abab65 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -106,6 +106,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 \
diff --git a/backends/platform/sdl/win32/win32.cpp b/backends/platform/sdl/win32/win32.cpp
index 0f70c00..fbab7eb 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 473e78f..ca0843e 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


Commit: e39faa239a8b0149d3be37e21afb173b936ab74c
    https://github.com/scummvm/scummvm/commit/e39faa239a8b0149d3be37e21afb173b936ab74c
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:56:56+01:00

Commit Message:
BACKENDS: Use a second thread to assist in buffering Win32 CD audio

Changed paths:
    backends/audiocd/win32/win32-audiocd.cpp



diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp
index fdde61c..6dd2b75 100644
--- a/backends/audiocd/win32/win32-audiocd.cpp
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -26,9 +26,11 @@
 #include "backends/audiocd/default/default-audiocd.h"
 #include "common/array.h"
 #include "common/config-manager.h"
-#include "common/endian.h"
-#include "common/str.h"
 #include "common/debug.h"
+#include "common/mutex.h"
+#include "common/queue.h"
+#include "common/str.h"
+#include "common/timer.h"
 
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
@@ -59,6 +61,11 @@ enum {
 	kPreGapFrames = kFramesPerSecond * 2
 };
 
+enum {
+	// Keep about a second's worth of audio in the buffer
+	kBufferThreshold = kFramesPerSecond
+};
+
 static int getFrameCount(const TRACK_DATA &data) {
 	int time = data.Address[1];
 	time *= kSecondsPerMinute;
@@ -71,6 +78,7 @@ static int getFrameCount(const TRACK_DATA &data) {
 class Win32AudioCDStream : public Audio::SeekableAudioStream {
 public:
 	Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry);
+	~Win32AudioCDStream();
 
 	int readBuffer(int16 *buffer, const int numSamples);
 	bool isStereo() const { return true; }
@@ -86,15 +94,26 @@ private:
 	int _frame;
 	uint _bufferPos;
 
+	Common::Queue<int16 *> _bufferQueue;
+	int _bufferFrame;
+	Common::Mutex _mutex;
+
 	bool readNextFrame();
+	static void timerProc(void *refCon);
+	void onTimer();
+	void emptyQueue();
+	void startTimer();
+	void stopTimer();
 };
 
 Win32AudioCDStream::Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry) :
-		_driveHandle(handle), _startEntry(startEntry), _endEntry(endEntry), _buffer(), _frame(0), _bufferPos(kSamplesPerFrame) {
-	// Read the first frame here. This should hopefully keep games more
-	// in sync instead of potentially waiting in the mixer thread for the
-	// disc to seek to the first frame.
-	readNextFrame();
+		_driveHandle(handle), _startEntry(startEntry), _endEntry(endEntry), _buffer(), _frame(0), _bufferPos(kSamplesPerFrame), _bufferFrame(0) {
+	startTimer();
+}
+
+Win32AudioCDStream::~Win32AudioCDStream() {
+	stopTimer();
+	emptyQueue();
 }
 
 int Win32AudioCDStream::readBuffer(int16 *buffer, const int numSamples) {
@@ -121,33 +140,21 @@ int Win32AudioCDStream::readBuffer(int16 *buffer, const int numSamples) {
 }
 
 bool Win32AudioCDStream::readNextFrame() {
-	// Figure out the MSF of the frame we're looking for
-	int frame = _frame + getFrameCount(_startEntry);
-
-	// Request to read that frame. Subtract the pre-gap frames
-	// so that we get to the right sector.
-	RAW_READ_INFO readAudio;
-	memset(&readAudio, 0, sizeof(readAudio));
-	readAudio.DiskOffset.QuadPart = (frame - kPreGapFrames) * 2048;
-	readAudio.SectorCount = 1;
-	readAudio.TrackMode = CDDA;
-	
-	DWORD bytesReturned;
-	bool result = DeviceIoControl(
-		_driveHandle,
-		IOCTL_CDROM_RAW_READ,
-		&readAudio,
-		sizeof(readAudio),
-		&_buffer,
-		sizeof(_buffer),
-		&bytesReturned,
-		NULL);
-	if (!result) {
-		warning("Failed to retrieve CD sector %d: %d", frame, (int)GetLastError());
-		_frame = getFrameCount(_endEntry);
-		return false;
+	// 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;
 }
@@ -157,10 +164,20 @@ bool Win32AudioCDStream::endOfData() const {
 }
 
 bool Win32AudioCDStream::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;
 }
 
@@ -168,6 +185,79 @@ Audio::Timestamp Win32AudioCDStream::getLength() const {
 	return Audio::Timestamp(0, getFrameCount(_endEntry) - getFrameCount(_startEntry), 75);
 }
 
+void Win32AudioCDStream::timerProc(void *refCon) {
+	static_cast<Win32AudioCDStream *>(refCon)->onTimer();
+}
+
+void Win32AudioCDStream::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 (getFrameCount(_startEntry) + _bufferFrame >= getFrameCount(_endEntry))
+		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 (queueCount < kBufferThreshold && getFrameCount(_startEntry) + _bufferFrame < getFrameCount(_endEntry)) {
+		int16 *buffer = new int16[kSamplesPerFrame];
+
+		// Figure out the MSF of the frame we're looking for
+		int frame = _bufferFrame + getFrameCount(_startEntry);
+
+		// 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;
+		bool result = DeviceIoControl(
+			_driveHandle,
+			IOCTL_CDROM_RAW_READ,
+			&readAudio,
+			sizeof(readAudio),
+			buffer,
+			kBytesPerFrame,
+			&bytesReturned,
+			NULL);
+		if (!result) {
+			warning("Failed to retrieve CD sector %d: %d", frame, (int)GetLastError());
+			_bufferFrame = getFrameCount(_endEntry) - getFrameCount(_startEntry);
+			return;
+		}
+
+		_bufferFrame++;
+
+		// Now push the buffer onto the queue
+		Common::StackLock lock(_mutex);
+		_bufferQueue.push(buffer);
+		queueCount = _bufferQueue.size();
+	}
+}
+
+void Win32AudioCDStream::startTimer() {
+	g_system->getTimerManager()->installTimerProc(timerProc, 10 * 1000, this, "Win32AudioCDStream");
+}
+
+void Win32AudioCDStream::stopTimer() {
+	g_system->getTimerManager()->removeTimerProc(timerProc);
+}
+
+void Win32AudioCDStream::emptyQueue() {
+	while (!_bufferQueue.empty())
+		delete[] _bufferQueue.pop();
+}
+
 class Win32AudioCDManager : public DefaultAudioCDManager {
 public:
 	Win32AudioCDManager();


Commit: 1626fbd633ff5e39ee5551b6e839def62ef9b599
    https://github.com/scummvm/scummvm/commit/1626fbd633ff5e39ee5551b6e839def62ef9b599
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:57:01+01:00

Commit Message:
BACKENDS: Allow for specifying a drive via the cdrom option

Changed paths:
    backends/audiocd/default/default-audiocd.cpp
    backends/audiocd/default/default-audiocd.h
    base/commandLine.cpp



diff --git a/backends/audiocd/default/default-audiocd.cpp b/backends/audiocd/default/default-audiocd.cpp
index 0c5bb8d..4c08938 100644
--- a/backends/audiocd/default/default-audiocd.cpp
+++ b/backends/audiocd/default/default-audiocd.cpp
@@ -155,5 +155,19 @@ DefaultAudioCDManager::Status DefaultAudioCDManager::getStatus() const {
 }
 
 bool DefaultAudioCDManager::openCD() {
-	return openCD(ConfMan.getInt("cdrom"));
+	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 6011a8f..5c7ee9e 100644
--- a/backends/audiocd/default/default-audiocd.h
+++ b/backends/audiocd/default/default-audiocd.h
@@ -26,6 +26,10 @@
 #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.
  */
@@ -51,12 +55,20 @@ public:
 	 * @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/base/commandLine.cpp b/base/commandLine.cpp
index 19702ea..c34e4d4 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"


Commit: 442f91c622075c026c1c2295d72d7f4b7e06b665
    https://github.com/scummvm/scummvm/commit/442f91c622075c026c1c2295d72d7f4b7e06b665
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:57:05+01:00

Commit Message:
BACKENDS: Add support for opening a CD on Linux by path or drive

Changed paths:
    backends/audiocd/linux/linux-audiocd.cpp



diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp
index 917546c..fe3ae1e 100644
--- a/backends/audiocd/linux/linux-audiocd.cpp
+++ b/backends/audiocd/linux/linux-audiocd.cpp
@@ -256,6 +256,9 @@ public:
 	void closeCD();
 	void playCD(int track, int numLoops, int startFrame, int duration);
 
+protected:
+	bool openCD(const Common::String &drive);
+
 private:
 	struct Device {
 		Device(const Common::String &n, dev_t d) : name(n), device(d) {}
@@ -320,6 +323,23 @@ bool LinuxAudioCDManager::openCD(int drive) {
 	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()) {
+		closeCD();
+		return false;
+	}
+
+	return true;
+}
+
 void LinuxAudioCDManager::closeCD() {
 	if (_fd < 0)
 		return;


Commit: 4a6c7b5c831f6d054033d9ded5cb83e4eb6f8d56
    https://github.com/scummvm/scummvm/commit/4a6c7b5c831f6d054033d9ded5cb83e4eb6f8d56
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:57:09+01:00

Commit Message:
BACKENDS: Add support for opening a CD on Windows by drive

Changed paths:
    backends/audiocd/win32/win32-audiocd.cpp



diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp
index 6dd2b75..5de7c7b 100644
--- a/backends/audiocd/win32/win32-audiocd.cpp
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -267,6 +267,9 @@ public:
 	void closeCD();
 	void playCD(int track, int numLoops, int startFrame, int duration);
 
+protected:
+	bool openCD(const Common::String &drive);
+
 private:
 	bool loadTOC();
 
@@ -314,6 +317,37 @@ bool Win32AudioCDManager::openCD(int drive) {
 	return false;
 }
 
+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()) {
+		closeCD();
+		return false;
+	}
+
+	return true;
+}
+
 void Win32AudioCDManager::closeCD() {
 	// Stop any previous track
 	stop();


Commit: dc0d4fcf303458e9044866dd05a30c317e07eef0
    https://github.com/scummvm/scummvm/commit/dc0d4fcf303458e9044866dd05a30c317e07eef0
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:57:13+01:00

Commit Message:
BACKENDS: Add support for opening a CD on Mac OS X by path or drive

Changed paths:
    backends/audiocd/macosx/macosx-audiocd.cpp



diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp
index a8ba712..76bae95 100644
--- a/backends/audiocd/macosx/macosx-audiocd.cpp
+++ b/backends/audiocd/macosx/macosx-audiocd.cpp
@@ -52,6 +52,7 @@ public:
 
 protected:
 	bool openCD(int drive);
+	bool openCD(const Common::String &drive);
 
 private:
 	struct Drive {
@@ -135,6 +136,24 @@ bool MacOSXAudioCDManager::openCD(int drive) {
 	return findTrackNames(cddaDrives[drive].mountPoint);
 }
 
+bool MacOSXAudioCDManager::openCD(const Common::String &drive) {
+	closeCD();
+
+	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::closeCD() {
 	stop();
 	_trackMap.clear();


Commit: aa6ff444408bfd17bcca1d8364e86ce62da327ef
    https://github.com/scummvm/scummvm/commit/aa6ff444408bfd17bcca1d8364e86ce62da327ef
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:57:19+01:00

Commit Message:
BACKENDS: Only expose one set of functions for AudioCDManager

Engines should only have to call one set of functions and not decide between the two. In fact, the 'emulation' API was documented to just call the 'real CD' API.

Changed paths:
    backends/audiocd/audiocd.h
    backends/audiocd/default/default-audiocd.cpp
    backends/audiocd/default/default-audiocd.h
    backends/audiocd/linux/linux-audiocd.cpp
    backends/audiocd/macosx/macosx-audiocd.cpp
    backends/audiocd/sdl/sdl-audiocd.cpp
    backends/audiocd/sdl/sdl-audiocd.h
    backends/audiocd/win32/win32-audiocd.cpp
    backends/platform/dc/dc.h
    backends/platform/dc/dcmain.cpp
    engines/agos/event.cpp
    engines/cine/main_loop.cpp
    engines/cine/sound.cpp
    engines/drascula/drascula.cpp
    engines/drascula/sound.cpp
    engines/gob/gob.cpp
    engines/groovie/groovie.cpp
    engines/kyra/sound_towns.cpp
    engines/made/made.cpp
    engines/sci/sound/audio.cpp
    engines/scumm/scumm.cpp
    engines/scumm/sound.cpp
    engines/teenagent/teenagent.cpp
    engines/tinsel/tinsel.cpp



diff --git a/backends/audiocd/audiocd.h b/backends/audiocd/audiocd.h
index 598893e..b3674f2 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,54 +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.
-	 * @return true if the CD drive was inited successfully
-	 */
-	virtual bool openCD() = 0;
-
-	/**
-	 * Close the currently open CD drive
-	 */
-	virtual void closeCD() = 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 4c08938..c2ce7ce 100644
--- a/backends/audiocd/default/default-audiocd.cpp
+++ b/backends/audiocd/default/default-audiocd.cpp
@@ -38,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;
@@ -56,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();
@@ -71,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() {
@@ -84,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() {
@@ -143,8 +137,6 @@ void DefaultAudioCDManager::update() {
 			// or not.
 			_emulating = false;
 		}
-	} else {
-		updateCD();
 	}
 }
 
@@ -154,7 +146,7 @@ DefaultAudioCDManager::Status DefaultAudioCDManager::getStatus() const {
 	return info;
 }
 
-bool DefaultAudioCDManager::openCD() {
+bool DefaultAudioCDManager::openRealCD() {
 	Common::String cdrom = ConfMan.get("cdrom");
 
 	// Try to parse it as an int
diff --git a/backends/audiocd/default/default-audiocd.h b/backends/audiocd/default/default-audiocd.h
index 5c7ee9e..e3fbb4b 100644
--- a/backends/audiocd/default/default-audiocd.h
+++ b/backends/audiocd/default/default-audiocd.h
@@ -36,18 +36,23 @@ class String;
 class DefaultAudioCDManager : public AudioCDManager {
 public:
 	DefaultAudioCDManager();
-	virtual ~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 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
 
-	bool openCD();
-	virtual void closeCD() {}
+protected:
+	/**
+	 * Open a CD using the cdrom config variable
+	 */
+	bool openRealCD();
 
 	/**
 	 * Open a CD using the specified drive index
@@ -56,12 +61,6 @@ public:
 	 */
 	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
diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp
index fe3ae1e..a835ade 100644
--- a/backends/audiocd/linux/linux-audiocd.cpp
+++ b/backends/audiocd/linux/linux-audiocd.cpp
@@ -252,11 +252,12 @@ public:
 	LinuxAudioCDManager();
 	~LinuxAudioCDManager();
 
-	bool openCD(int drive);
-	void closeCD();
-	void playCD(int track, int numLoops, int startFrame, int duration);
+	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:
@@ -301,22 +302,40 @@ LinuxAudioCDManager::LinuxAudioCDManager() {
 }
 
 LinuxAudioCDManager::~LinuxAudioCDManager() {
-	closeCD();
+	close();
 }
 
-bool LinuxAudioCDManager::openCD(int drive) {
-	closeCD();
+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);
+	_fd = ::open(devices[drive].name.c_str(), O_RDONLY | O_NONBLOCK, 0);
 	if (_fd < 0)
 		return false;
 
 	if (!loadTOC()) {
-		closeCD();
+		close();
 		return false;
 	}
 
@@ -328,44 +347,39 @@ bool LinuxAudioCDManager::openCD(const Common::String &drive) {
 	if (!tryAddDrive(devices, drive) && !tryAddPath(devices, drive))
 		return false;
 
-	_fd = open(devices[0].name.c_str(), O_RDONLY | O_NONBLOCK, 0);
+	_fd = ::open(devices[0].name.c_str(), O_RDONLY | O_NONBLOCK, 0);
 	if (_fd < 0)
 		return false;
 
 	if (!loadTOC()) {
-		closeCD();
+		close();
 		return false;
 	}
 
 	return true;
 }
 
-void LinuxAudioCDManager::closeCD() {
-	if (_fd < 0)
-		return;
-
-	stop();
-	close(_fd);
-	memset(&_tocHeader, 0, sizeof(_tocHeader));
-	_tocEntries.clear();
-}
+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;
 
-void LinuxAudioCDManager::playCD(int track, int numLoops, int startFrame, int duration) {
-	// Stop any previous track
-	stop();
+	// 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;
+		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;
+		return false;
 	}
 
 	// Create the AudioStream and play it
@@ -388,6 +402,8 @@ void LinuxAudioCDManager::playCD(int track, int numLoops, int startFrame, int du
 		_cd.balance,
 		DisposeAfterUse::YES,
 		true);
+
+	return true;
 }
 
 LinuxAudioCDManager::DeviceList LinuxAudioCDManager::scanDevices() {
@@ -421,13 +437,13 @@ bool LinuxAudioCDManager::tryAddDrive(DeviceList &devices, const Common::String
 		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);
+	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);
+		::close(fd);
 		if (isCD) {
 			devices.push_back(Device(drive, device));
 			return true;
diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp
index 76bae95..07d8d31 100644
--- a/backends/audiocd/macosx/macosx-audiocd.cpp
+++ b/backends/audiocd/macosx/macosx-audiocd.cpp
@@ -47,8 +47,9 @@ public:
 	MacOSXAudioCDManager() {}
 	~MacOSXAudioCDManager();
 
-	void playCD(int track, int num_loops, int start_frame, int duration);
-	void closeCD();
+	bool open();
+	void close();
+	bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false);
 
 protected:
 	bool openCD(int drive);
@@ -74,7 +75,16 @@ private:
 };
 
 MacOSXAudioCDManager::~MacOSXAudioCDManager() {
-	closeCD();
+	close();
+}
+
+bool MacOSXAudioCDManager::open() {
+	close();
+
+	if (openRealCD())
+		return true;
+
+	return DefaultAudioCDManager::open();
 }
 
 /**
@@ -95,8 +105,6 @@ static int findBaseDiskNumber(const Common::String &diskName) {
 }
 
 bool MacOSXAudioCDManager::openCD(int drive) {
-	closeCD();
-
 	DriveList allDrives = detectAllDrives();
 	if (allDrives.empty())
 		return false;
@@ -137,8 +145,6 @@ bool MacOSXAudioCDManager::openCD(int drive) {
 }
 
 bool MacOSXAudioCDManager::openCD(const Common::String &drive) {
-	closeCD();
-
 	DriveList drives = detectAllDrives();
 
 	for (uint32 i = 0; i < drives.size(); i++) {
@@ -154,8 +160,8 @@ bool MacOSXAudioCDManager::openCD(const Common::String &drive) {
 	return false;
 }
 
-void MacOSXAudioCDManager::closeCD() {
-	stop();
+void MacOSXAudioCDManager::close() {
+	DefaultAudioCDManager::close();
 	_trackMap.clear();
 }
 
@@ -178,9 +184,17 @@ MacOSXAudioCDManager::DriveList MacOSXAudioCDManager::detectAllDrives() {
 	return drives;
 }
 
-void MacOSXAudioCDManager::playCD(int track, int numLoops, int startFrame, int duration) {
-	if (!_trackMap.contains(track) || (!numLoops && !startFrame))
-		return;
+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];
@@ -188,19 +202,19 @@ void MacOSXAudioCDManager::playCD(int track, int numLoops, int startFrame, int d
 
 	if (!stream) {
 		warning("Failed to open track '%s'", fileName.c_str());
-		return;
+		return false;
 	}
 
 	Audio::AudioStream *audioStream = Audio::makeAIFFStream(stream, DisposeAfterUse::YES);
 	if (!audioStream) {
 		warning("Track '%s' is not an AIFF track", fileName.c_str());
-		return;
+		return false;
 	}
 
 	Audio::SeekableAudioStream *seekStream = dynamic_cast<Audio::SeekableAudioStream *>(audioStream);
 	if (!seekStream) {
 		warning("Track '%s' is not seekable", fileName.c_str());
-		return;
+		return false;
 	}
 
 	Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75);
@@ -211,6 +225,7 @@ void MacOSXAudioCDManager::playCD(int track, int numLoops, int startFrame, int d
 
 	_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) {
diff --git a/backends/audiocd/sdl/sdl-audiocd.cpp b/backends/audiocd/sdl/sdl-audiocd.cpp
index d745f29..3558fb5 100644
--- a/backends/audiocd/sdl/sdl-audiocd.cpp
+++ b/backends/audiocd/sdl/sdl-audiocd.cpp
@@ -43,7 +43,16 @@ SdlAudioCDManager::SdlAudioCDManager()
 }
 
 SdlAudioCDManager::~SdlAudioCDManager() {
-	closeCD();
+	close();
+}
+
+bool SdlAudioCDManager::open() {
+	close();
+
+	if (openRealCD())
+		return true;
+
+	return DefaultAudioCDManager::open();
 }
 
 bool SdlAudioCDManager::openCD(int drive) {
@@ -64,7 +73,9 @@ bool SdlAudioCDManager::openCD(int drive) {
 	return (_cdrom != NULL);
 }
 
-void SdlAudioCDManager::closeCD() {
+void SdlAudioCDManager::close() {
+	DefaultAudioCDManager::close();
+
 	if (_cdrom) {
                 SDL_CDStop(_cdrom);
                 SDL_CDClose(_cdrom);
@@ -72,44 +83,59 @@ void SdlAudioCDManager::closeCD() {
         }
 }
 
-void SdlAudioCDManager::stopCD() {
+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 4ece8dd..91895da 100644
--- a/backends/audiocd/sdl/sdl-audiocd.h
+++ b/backends/audiocd/sdl/sdl-audiocd.h
@@ -37,13 +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 closeCD();
-	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/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp
index 5de7c7b..e3fdcf6 100644
--- a/backends/audiocd/win32/win32-audiocd.cpp
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -263,11 +263,12 @@ public:
 	Win32AudioCDManager();
 	~Win32AudioCDManager();
 
-	bool openCD(int drive);
-	void closeCD();
-	void playCD(int track, int numLoops, int startFrame, int duration);
+	bool open();
+	void close();
+	void play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false);
 
 protected:
+	bool openCD(int drive);
 	bool openCD(const Common::String &drive);
 
 private:
@@ -288,12 +289,19 @@ Win32AudioCDManager::Win32AudioCDManager() {
 }
 
 Win32AudioCDManager::~Win32AudioCDManager() {
-	closeCD();
+	close();
 }
 
-bool Win32AudioCDManager::openCD(int drive) {
-	closeCD();
+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())
@@ -310,11 +318,11 @@ bool Win32AudioCDManager::openCD(int drive) {
 	}
 
 	if (!loadTOC()) {
-		closeCD();
+		close();
 		return false;
 	}
 	
-	return false;
+	return true;
 }
 
 bool Win32AudioCDManager::openCD(const Common::String &drive) {
@@ -341,16 +349,15 @@ bool Win32AudioCDManager::openCD(const Common::String &drive) {
 	}
 
 	if (!loadTOC()) {
-		closeCD();
+		close();
 		return false;
 	}
 
 	return true;
 }
 
-void Win32AudioCDManager::closeCD() {
-	// Stop any previous track
-	stop();
+void Win32AudioCDManager::close() {
+	DefaultAudioCDManager::close();
 
 	if (_driveHandle != INVALID_HANDLE_VALUE) {
 		CloseHandle(_driveHandle);
@@ -361,22 +368,27 @@ void Win32AudioCDManager::closeCD() {
 	_tocEntries.clear();
 }
 
-void Win32AudioCDManager::playCD(int track, int numLoops, int startFrame, int duration) {
-	// Stop any previous track
-	stop();
+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;
+		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;
+		return false;
 	}
 
 	// Create the AudioStream and play it
@@ -399,6 +411,7 @@ void Win32AudioCDManager::playCD(int track, int numLoops, int startFrame, int du
 		_cd.balance,
 		DisposeAfterUse::YES,
 		true);
+	return true;
 }
 
 bool Win32AudioCDManager::loadTOC() {
diff --git a/backends/platform/dc/dc.h b/backends/platform/dc/dc.h
index b490803..b567142 100644
--- a/backends/platform/dc/dc.h
+++ b/backends/platform/dc/dc.h
@@ -57,24 +57,16 @@ class DCHardware {
 };
 
 class DCCDManager : public DefaultAudioCDManager {
-  // Initialize the specified CD drive for audio playback.
-  bool openCD();
-
-	// Close the open CD drive
-	void closeCD() {}
-
-  // 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 aa8430a..bd66b81 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()
-{
-  // 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/engines/agos/event.cpp b/engines/agos/event.cpp
index 95bcc68..5240cdd 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/cine/main_loop.cpp b/engines/cine/main_loop.cpp
index e52fc46..19a2d8a 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 0e7da2e..a8b4c08 100644
--- a/engines/cine/sound.cpp
+++ b/engines/cine/sound.cpp
@@ -942,7 +942,7 @@ PCSound::PCSound(Audio::Mixer *mixer, CineEngine *vm)
 
 	// Ensure the CD is open
 	if (_vm->getGameType() == GType_FW && (_vm->getFeatures() & GF_CD))
-		g_system->getAudioCDManager()->openCD();
+		g_system->getAudioCDManager()->open();
 }
 
 PCSound::~PCSound() {
diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp
index 1a7659f..9ac9031 100644
--- a/engines/drascula/drascula.cpp
+++ b/engines/drascula/drascula.cpp
@@ -183,7 +183,7 @@ DrasculaEngine::DrasculaEngine(OSystem *syst, const DrasculaGameDescription *gam
 	const Common::FSNode gameDataDir(ConfMan.get("path"));
 	SearchMan.addSubDirectoryMatching(gameDataDir, "audio");
 
-	_system->getAudioCDManager()->openCD();
+	_system->getAudioCDManager()->open();
 
 	_lang = kEnglish;
 
diff --git a/engines/drascula/sound.cpp b/engines/drascula/sound.cpp
index 148dae7..c576b37 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 df2d804..d995f26 100644
--- a/engines/gob/gob.cpp
+++ b/engines/gob/gob.cpp
@@ -296,7 +296,7 @@ Common::Error GobEngine::run() {
 	if (isCD())
 		checkCD();
 
-	_system->getAudioCDManager()->openCD();
+	_system->getAudioCDManager()->open();
 
 	_global->_debugFlag = 1;
 	_video->_doRangeClamp = true;
diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp
index a25bf00..bbc290e 100644
--- a/engines/groovie/groovie.cpp
+++ b/engines/groovie/groovie.cpp
@@ -257,7 +257,7 @@ Common::Error GroovieEngine::run() {
 	// the same cd
 	if (getPlatform() != Common::kPlatformIOS) {
 		checkCD();
-		_system->getAudioCDManager()->openCD();
+		_system->getAudioCDManager()->open();
 	}
 
 	while (!shouldQuit()) {
diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp
index b269cb4..646f908 100644
--- a/engines/kyra/sound_towns.cpp
+++ b/engines/kyra/sound_towns.cpp
@@ -69,13 +69,13 @@ bool SoundTowns::init() {
 	_player->driver()->setOutputVolume(1, 118, 118);
 
 	// Initialize CD for audio
-	g_system->getAudioCDManager()->openCD();
+	g_system->getAudioCDManager()->open();
 
 	return true;
 }
 
 void SoundTowns::process() {
-	g_system->getAudioCDManager()->updateCD();
+	g_system->getAudioCDManager()->update();
 }
 
 void SoundTowns::playTrack(uint8 track) {
@@ -98,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);
@@ -111,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++)
@@ -412,7 +412,7 @@ bool SoundPC98::init() {
 	updateVolumeSettings();
 
 	// Initialize CD for audio
-	g_system->getAudioCDManager()->openCD();
+	g_system->getAudioCDManager()->open();
 
 	return reslt;
 }
@@ -478,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();
 }
 
@@ -538,7 +538,7 @@ bool SoundTownsPC98_v2::init() {
 				_vm->checkCD();
 
 		// Initialize CD for audio
-		bool hasRealCD = g_system->getAudioCDManager()->openCD();
+		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
@@ -591,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) {
@@ -621,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();
 	}
@@ -632,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 57130e2..f153929 100644
--- a/engines/made/made.cpp
+++ b/engines/made/made.cpp
@@ -67,7 +67,7 @@ MadeEngine::MadeEngine(OSystem *syst, const MadeGameDescription *gameDesc) : Eng
 
 	_console = new MadeConsole(this);
 
-	_system->getAudioCDManager()->openCD();
+	_system->getAudioCDManager()->open();
 
 	_pmvPlayer = new PmvPlayer(this, _mixer);
 	_res = new ResourceReader();
@@ -268,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 5e5e8b0..a74bfa2 100644
--- a/engines/sci/sound/audio.cpp
+++ b/engines/sci/sound/audio.cpp
@@ -514,7 +514,7 @@ 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()->openCD();
+		g_system->getAudioCDManager()->open();
 		_initCD = true;
 	}
 
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 680bdb2..d9148ed 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1275,7 +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();
-		_system->getAudioCDManager()->openCD();
+		_system->getAudioCDManager()->open();
 	}
 
 	// Create the sound manager
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 4d70ee8..404bdd0 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 f5d9f72..4dc7857 100644
--- a/engines/teenagent/teenagent.cpp
+++ b/engines/teenagent/teenagent.cpp
@@ -547,7 +547,7 @@ Common::Error TeenAgentEngine::run() {
 
 	// Initialize CD audio
 	if (_gameDescription->flags & ADGF_CD)
-		g_system->getAudioCDManager()->openCD();
+		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 782dd7a..1b733a0 100644
--- a/engines/tinsel/tinsel.cpp
+++ b/engines/tinsel/tinsel.cpp
@@ -841,7 +841,7 @@ TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc)
 		if (!scumm_stricmp(g->gameid, gameid))
 			_gameId = g->id;
 
-	_system->getAudioCDManager()->openCD();
+	_system->getAudioCDManager()->open();
 
 	_mousePos.x = 0;
 	_mousePos.y = 0;
@@ -973,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();
 		}
 


Commit: 41a1dcb0bb8bd7aaa20d03ea92aa49210b66fa92
    https://github.com/scummvm/scummvm/commit/41a1dcb0bb8bd7aaa20d03ea92aa49210b66fa92
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:57:24+01:00

Commit Message:
BACKENDS: Switch to a common base class for threaded audio CD streams

Changed paths:
  A backends/audiocd/audiocd-stream.cpp
  A backends/audiocd/audiocd-stream.h
    backends/audiocd/linux/linux-audiocd.cpp
    backends/audiocd/win32/win32-audiocd.cpp
    backends/module.mk



diff --git a/backends/audiocd/audiocd-stream.cpp b/backends/audiocd/audiocd-stream.cpp
new file mode 100644
index 0000000..da55654
--- /dev/null
+++ b/backends/audiocd/audiocd-stream.cpp
@@ -0,0 +1,174 @@
+/* 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() {
+	_forceStop = false;
+	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 0000000..a893802
--- /dev/null
+++ b/backends/audiocd/audiocd-stream.h
@@ -0,0 +1,86 @@
+/* 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();
+	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/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp
index a835ade..ea5eb1f 100644
--- a/backends/audiocd/linux/linux-audiocd.cpp
+++ b/backends/audiocd/linux/linux-audiocd.cpp
@@ -29,15 +29,12 @@
 
 #include "backends/audiocd/linux/linux-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/mutex.h"
-#include "common/queue.h"
 #include "common/str.h"
 #include "common/debug.h"
-#include "common/timer.h"
 
 enum {
 	kLeadoutTrack = 0xAA
@@ -67,185 +64,59 @@ static int getFrameCount(const cdrom_msf0 &msf) {
 	return time;
 }
 
-class LinuxAudioCDStream : public Audio::SeekableAudioStream {
+class LinuxAudioCDStream : public AudioCDStream {
 public:
 	LinuxAudioCDStream(int fd, const cdrom_tocentry &startEntry, const cdrom_tocentry &endEntry);
 	~LinuxAudioCDStream();
 
-	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:
+	uint getStartFrame() const;
+	uint getEndFrame() const;
+	bool readFrame(int frame, int16 *buffer);
 
 private:
 	int _fd;
 	const cdrom_tocentry &_startEntry, &_endEntry;
-	int16 _buffer[kSamplesPerFrame];
-	int _frame;
-	uint _bufferPos;
-
-	Common::Queue<int16 *> _bufferQueue;
-	int _bufferFrame;
-	Common::Mutex _mutex;
-
-	bool readNextFrame();
-	static void timerProc(void *refCon);
-	void onTimer();
-	void emptyQueue();
-	void startTimer();
-	void stopTimer();
 };
 
 LinuxAudioCDStream::LinuxAudioCDStream(int fd, const cdrom_tocentry &startEntry, const cdrom_tocentry &endEntry) :
-		_fd(fd), _startEntry(startEntry), _endEntry(endEntry), _buffer(), _frame(0), _bufferPos(kSamplesPerFrame), _bufferFrame(0) {
+		_fd(fd), _startEntry(startEntry), _endEntry(endEntry) {
 	startTimer();
 }
 
 LinuxAudioCDStream::~LinuxAudioCDStream() {
 	stopTimer();
-	emptyQueue();
 }
 
-int LinuxAudioCDStream::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 LinuxAudioCDStream::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 LinuxAudioCDStream::endOfData() const {
-	return getFrameCount(_startEntry.cdte_addr.msf) + _frame >= getFrameCount(_endEntry.cdte_addr.msf) && _bufferPos == kSamplesPerFrame;
-}
-
-bool LinuxAudioCDStream::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;
+bool LinuxAudioCDStream::readFrame(int frame, int16 *buffer) {
+	int seconds = frame / kFramesPerSecond;
+	frame %= kFramesPerSecond;
+	int minutes = seconds / kSecondsPerMinute;
+	seconds %= kSecondsPerMinute;
+
+	// Request to read that frame
+	cdrom_read_audio readAudio;
+	readAudio.addr.msf.minute = minutes;
+	readAudio.addr.msf.second = seconds;
+	readAudio.addr.msf.frame = frame;
+	readAudio.addr_format = CDROM_MSF;
+	readAudio.nframes = 1;
+	readAudio.buf = reinterpret_cast<__u8*>(buffer);
+
+	if (ioctl(_fd, CDROMREADAUDIO, &readAudio) < 0)
+		return false;
 
-	// Start the timer again
-	startTimer();
 	return true;
 }
 
-Audio::Timestamp LinuxAudioCDStream::getLength() const {
-	return Audio::Timestamp(0, getFrameCount(_endEntry.cdte_addr.msf) - getFrameCount(_startEntry.cdte_addr.msf), 75);
+uint LinuxAudioCDStream::getStartFrame() const {
+	return getFrameCount(_startEntry.cdte_addr.msf);
 }
 
-void LinuxAudioCDStream::timerProc(void *refCon) {
-	static_cast<LinuxAudioCDStream *>(refCon)->onTimer();
+uint LinuxAudioCDStream::getEndFrame() const {
+	return getFrameCount(_endEntry.cdte_addr.msf);
 }
 
-void LinuxAudioCDStream::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 (getFrameCount(_startEntry.cdte_addr.msf) + _bufferFrame >= getFrameCount(_endEntry.cdte_addr.msf))
-		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 (queueCount < kBufferThreshold && getFrameCount(_startEntry.cdte_addr.msf) + _bufferFrame < getFrameCount(_endEntry.cdte_addr.msf)) {
-		int16 *buffer = new int16[kSamplesPerFrame];
-
-		// Figure out the MSF of the frame we're looking for
-		int frame = _bufferFrame + getFrameCount(_startEntry.cdte_addr.msf);
-
-		int seconds = frame / kFramesPerSecond;
-		frame %= kFramesPerSecond;
-		int minutes = seconds / kSecondsPerMinute;
-		seconds %= kSecondsPerMinute;
-
-		// Request to read that frame
-		cdrom_read_audio readAudio;
-		readAudio.addr.msf.minute = minutes;
-		readAudio.addr.msf.second = seconds;
-		readAudio.addr.msf.frame = frame;
-		readAudio.addr_format = CDROM_MSF;
-		readAudio.nframes = 1;
-		readAudio.buf = reinterpret_cast<__u8*>(buffer);
-
-		if (ioctl(_fd, CDROMREADAUDIO, &readAudio) < 0) {
-			warning("Failed to read audio");
-			_bufferFrame = getFrameCount(_endEntry.cdte_addr.msf);
-			return;
-		}
-
-		_bufferFrame++;
-
-		// Now push the buffer onto the queue
-		Common::StackLock lock(_mutex);
-		_bufferQueue.push(buffer);
-		queueCount = _bufferQueue.size();
-	}
-}
-
-void LinuxAudioCDStream::startTimer() {
-	g_system->getTimerManager()->installTimerProc(timerProc, 10 * 1000, this, "LinuxAudioCDStream");
-}
-
-void LinuxAudioCDStream::stopTimer() {
-	g_system->getTimerManager()->removeTimerProc(timerProc);
-}
-
-void LinuxAudioCDStream::emptyQueue() {
-	while (!_bufferQueue.empty())
-		delete[] _bufferQueue.pop();
-}
 
 class LinuxAudioCDManager : public DefaultAudioCDManager {
 public:
diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp
index e3fdcf6..0b04a7c 100644
--- a/backends/audiocd/win32/win32-audiocd.cpp
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -23,6 +23,7 @@
 #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"
@@ -43,29 +44,10 @@
 #endif
 
 enum {
-	kLeadoutTrack = 0xAA
-};
-
-enum {
-	kBytesPerFrame = 2352,
-	kSamplesPerFrame = kBytesPerFrame / 2
-};
-
-enum {
-	kSecondsPerMinute = 60,
-	kFramesPerSecond = 75
-};
-
-enum {
 	// The CD-ROM pre-gap is 2s
 	kPreGapFrames = kFramesPerSecond * 2
 };
 
-enum {
-	// Keep about a second's worth of audio in the buffer
-	kBufferThreshold = kFramesPerSecond
-};
-
 static int getFrameCount(const TRACK_DATA &data) {
 	int time = data.Address[1];
 	time *= kSecondsPerMinute;
@@ -75,35 +57,19 @@ static int getFrameCount(const TRACK_DATA &data) {
 	return time;
 }
 
-class Win32AudioCDStream : public Audio::SeekableAudioStream {
+class Win32AudioCDStream : public AudioCDStream {
 public:
 	Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry);
 	~Win32AudioCDStream();
 
-	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:
+	uint getStartFrame() const;
+	uint getEndFrame() const;
+	bool readFrame(int frame, int16 *buffer);
 
 private:
 	HANDLE _driveHandle;
 	const TRACK_DATA &_startEntry, &_endEntry;
-	int16 _buffer[kSamplesPerFrame];
-	int _frame;
-	uint _bufferPos;
-
-	Common::Queue<int16 *> _bufferQueue;
-	int _bufferFrame;
-	Common::Mutex _mutex;
-
-	bool readNextFrame();
-	static void timerProc(void *refCon);
-	void onTimer();
-	void emptyQueue();
-	void startTimer();
-	void stopTimer();
 };
 
 Win32AudioCDStream::Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry) :
@@ -113,150 +79,36 @@ Win32AudioCDStream::Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEnt
 
 Win32AudioCDStream::~Win32AudioCDStream() {
 	stopTimer();
-	emptyQueue();
-}
-
-int Win32AudioCDStream::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 Win32AudioCDStream::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 Win32AudioCDStream::endOfData() const {
-	return getFrameCount(_startEntry) + _frame >= getFrameCount(_endEntry) && _bufferPos == kSamplesPerFrame;
-}
-
-bool Win32AudioCDStream::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 Win32AudioCDStream::getLength() const {
-	return Audio::Timestamp(0, getFrameCount(_endEntry) - getFrameCount(_startEntry), 75);
+uint Win32AudioCDStream::getStartFrame() const {
+	return getFrameCount(_startEntry);
 }
 
-void Win32AudioCDStream::timerProc(void *refCon) {
-	static_cast<Win32AudioCDStream *>(refCon)->onTimer();
-}
-
-void Win32AudioCDStream::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 (getFrameCount(_startEntry) + _bufferFrame >= getFrameCount(_endEntry))
-		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 (queueCount < kBufferThreshold && getFrameCount(_startEntry) + _bufferFrame < getFrameCount(_endEntry)) {
-		int16 *buffer = new int16[kSamplesPerFrame];
-
-		// Figure out the MSF of the frame we're looking for
-		int frame = _bufferFrame + getFrameCount(_startEntry);
-
-		// 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;
-		bool result = DeviceIoControl(
-			_driveHandle,
-			IOCTL_CDROM_RAW_READ,
-			&readAudio,
-			sizeof(readAudio),
-			buffer,
-			kBytesPerFrame,
-			&bytesReturned,
-			NULL);
-		if (!result) {
-			warning("Failed to retrieve CD sector %d: %d", frame, (int)GetLastError());
-			_bufferFrame = getFrameCount(_endEntry) - getFrameCount(_startEntry);
-			return;
-		}
-
-		_bufferFrame++;
-
-		// Now push the buffer onto the queue
-		Common::StackLock lock(_mutex);
-		_bufferQueue.push(buffer);
-		queueCount = _bufferQueue.size();
-	}
+uint Win32AudioCDStream::getEndFrame() const {
+	return getFrameCount(_endFrame);
 }
 
-void Win32AudioCDStream::startTimer() {
-	g_system->getTimerManager()->installTimerProc(timerProc, 10 * 1000, this, "Win32AudioCDStream");
-}
+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;
 
-void Win32AudioCDStream::stopTimer() {
-	g_system->getTimerManager()->removeTimerProc(timerProc);
+	DWORD bytesReturned;
+	return DeviceIoControl(
+		_driveHandle,
+		IOCTL_CDROM_RAW_READ,
+		&readAudio,
+		sizeof(readAudio),
+		buffer,
+		kBytesPerFrame,
+		&bytesReturned,
+		NULL);
 }
 
-void Win32AudioCDStream::emptyQueue() {
-	while (!_bufferQueue.empty())
-		delete[] _bufferQueue.pop();
-}
 
 class Win32AudioCDManager : public DefaultAudioCDManager {
 public:
diff --git a/backends/module.mk b/backends/module.mk
index 8abab65..3f7620e 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 \


Commit: c6c0f527a7394d70afd4a50d8b9b41d035bbe7b8
    https://github.com/scummvm/scummvm/commit/c6c0f527a7394d70afd4a50d8b9b41d035bbe7b8
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2016-03-13T13:57:30+01:00

Commit Message:
BACKENDS: Switch to CDROMREADRAW for Linux audio CD

CDROMREADAUDIO seems to crash the kernel when ejecting while playing. Let's try to avoid that.

Changed paths:
    backends/audiocd/linux/linux-audiocd.cpp



diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp
index ea5eb1f..b48ce78 100644
--- a/backends/audiocd/linux/linux-audiocd.cpp
+++ b/backends/audiocd/linux/linux-audiocd.cpp
@@ -64,6 +64,20 @@ static int getFrameCount(const cdrom_msf0 &msf) {
 	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);
@@ -89,23 +103,33 @@ LinuxAudioCDStream::~LinuxAudioCDStream() {
 }
 
 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
-	cdrom_read_audio readAudio;
-	readAudio.addr.msf.minute = minutes;
-	readAudio.addr.msf.second = seconds;
-	readAudio.addr.msf.frame = frame;
-	readAudio.addr_format = CDROM_MSF;
-	readAudio.nframes = 1;
-	readAudio.buf = reinterpret_cast<__u8*>(buffer);
-
-	if (ioctl(_fd, CDROMREADAUDIO, &readAudio) < 0)
+	// 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;
 }
 


Commit: 90899860b4e4843cc42b272c86d15951c8846dca
    https://github.com/scummvm/scummvm/commit/90899860b4e4843cc42b272c86d15951c8846dca
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2016-03-13T14:06:20+01:00

Commit Message:
BACKENDS: Formatting fixes.

Changed paths:
    backends/audiocd/audiocd-stream.cpp
    backends/audiocd/audiocd-stream.h
    backends/audiocd/linux/linux-audiocd.cpp
    backends/audiocd/macosx/macosx-audiocd.cpp
    backends/audiocd/win32/win32-audiocd.cpp



diff --git a/backends/audiocd/audiocd-stream.cpp b/backends/audiocd/audiocd-stream.cpp
index da55654..7265b7f 100644
--- a/backends/audiocd/audiocd-stream.cpp
+++ b/backends/audiocd/audiocd-stream.cpp
@@ -51,7 +51,7 @@ int AudioCDStream::readBuffer(int16 *buffer, const int numSamples) {
 			return samples;
 
 		// Copy the samples over
-		for (_bufferPos = 0; _bufferPos < kSamplesPerFrame && samples < numSamples; )
+		for (_bufferPos = 0; _bufferPos < kSamplesPerFrame && samples < numSamples;)
 			buffer[samples++] = _buffer[_bufferPos++];
 	}
 
diff --git a/backends/audiocd/audiocd-stream.h b/backends/audiocd/audiocd-stream.h
index a893802..e63c060 100644
--- a/backends/audiocd/audiocd-stream.h
+++ b/backends/audiocd/audiocd-stream.h
@@ -70,17 +70,17 @@ private:
 
 	Common::Queue<int16 *> _bufferQueue;
 	int _bufferFrame;
-        Common::Mutex _mutex;
+	Common::Mutex _mutex;
 
-        bool _forceStop;
-        bool shouldForceStop() const;
-        void forceStop();
-        Common::Mutex _forceStopMutex;
+	bool _forceStop;
+	bool shouldForceStop() const;
+	void forceStop();
+	Common::Mutex _forceStopMutex;
 
-        bool readNextFrame();
-        static void timerProc(void *refCon);
-        void onTimer();
-        void emptyQueue();
+	bool readNextFrame();
+	static void timerProc(void *refCon);
+	void onTimer();
+	void emptyQueue();
 };
 
 #endif
diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp
index b48ce78..b3d355f 100644
--- a/backends/audiocd/linux/linux-audiocd.cpp
+++ b/backends/audiocd/linux/linux-audiocd.cpp
@@ -94,7 +94,7 @@ private:
 };
 
 LinuxAudioCDStream::LinuxAudioCDStream(int fd, const cdrom_tocentry &startEntry, const cdrom_tocentry &endEntry) :
-		_fd(fd), _startEntry(startEntry), _endEntry(endEntry) {
+	_fd(fd), _startEntry(startEntry), _endEntry(endEntry) {
 	startTimer();
 }
 
@@ -264,7 +264,7 @@ bool LinuxAudioCDManager::play(int track, int numLoops, int startFrame, int dura
 		return false;
 
 	// HACK: For now, just assume that track number is right
-	// That only works because ScummVM uses the wrong track number anyway	
+	// That only works because ScummVM uses the wrong track number anyway
 
 	if (track >= (int)_tocEntries.size() - 1) {
 		warning("No such track %d", track);
@@ -289,14 +289,14 @@ bool LinuxAudioCDManager::play(int track, int numLoops, int startFrame, int dura
 	_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);
+	    Audio::Mixer::kMusicSoundType,
+	    &_handle,
+	    Audio::makeLoopingAudioStream(audioStream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops),
+	    -1,
+	    _cd.volume,
+	    _cd.balance,
+	    DisposeAfterUse::YES,
+	    true);
 
 	return true;
 }
diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp
index 07d8d31..d6ff0be 100644
--- a/backends/audiocd/macosx/macosx-audiocd.cpp
+++ b/backends/audiocd/macosx/macosx-audiocd.cpp
@@ -224,7 +224,7 @@ bool MacOSXAudioCDManager::play(int track, int numLoops, int startFrame, int dur
 	_emulating = true;
 
 	_mixer->playStream(Audio::Mixer::kMusicSoundType, &_handle,
-			Audio::makeLoopingAudioStream(seekStream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops), -1, _cd.volume, _cd.balance);
+	                   Audio::makeLoopingAudioStream(seekStream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops), -1, _cd.volume, _cd.balance);
 	return true;
 }
 
diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp
index 0b04a7c..d974746 100644
--- a/backends/audiocd/win32/win32-audiocd.cpp
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -73,7 +73,7 @@ private:
 };
 
 Win32AudioCDStream::Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry) :
-		_driveHandle(handle), _startEntry(startEntry), _endEntry(endEntry), _buffer(), _frame(0), _bufferPos(kSamplesPerFrame), _bufferFrame(0) {
+	_driveHandle(handle), _startEntry(startEntry), _endEntry(endEntry), _buffer(), _frame(0), _bufferPos(kSamplesPerFrame), _bufferFrame(0) {
 	startTimer();
 }
 
@@ -99,14 +99,14 @@ bool Win32AudioCDStream::readFrame(int frame, int16 *buffer) {
 
 	DWORD bytesReturned;
 	return DeviceIoControl(
-		_driveHandle,
-		IOCTL_CDROM_RAW_READ,
-		&readAudio,
-		sizeof(readAudio),
-		buffer,
-		kBytesPerFrame,
-		&bytesReturned,
-		NULL);
+	           _driveHandle,
+	           IOCTL_CDROM_RAW_READ,
+	           &readAudio,
+	           sizeof(readAudio),
+	           buffer,
+	           kBytesPerFrame,
+	           &bytesReturned,
+	           NULL);
 }
 
 
@@ -173,7 +173,7 @@ bool Win32AudioCDManager::openCD(int drive) {
 		close();
 		return false;
 	}
-	
+
 	return true;
 }
 
@@ -230,7 +230,7 @@ bool Win32AudioCDManager::play(int track, int numLoops, int startFrame, int dura
 		return false;
 
 	// HACK: For now, just assume that track number is right
-	// That only works because ScummVM uses the wrong track number anyway	
+	// That only works because ScummVM uses the wrong track number anyway
 
 	if (track >= (int)_tocEntries.size() - 1) {
 		warning("No such track %d", track);
@@ -255,14 +255,14 @@ bool Win32AudioCDManager::play(int track, int numLoops, int startFrame, int dura
 	_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);
+	    Audio::Mixer::kMusicSoundType,
+	    &_handle,
+	    Audio::makeLoopingAudioStream(audioStream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops),
+	    -1,
+	    _cd.volume,
+	    _cd.balance,
+	    DisposeAfterUse::YES,
+	    true);
 	return true;
 }
 
@@ -272,23 +272,23 @@ bool Win32AudioCDManager::loadTOC() {
 	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);
+	                  _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


Commit: b32f347531cf9aa5b23db5ce875b5b7a295022bb
    https://github.com/scummvm/scummvm/commit/b32f347531cf9aa5b23db5ce875b5b7a295022bb
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2016-03-13T14:07:56+01:00

Commit Message:
SDL: Get rid of reference to SDL 1.3.

We only support SDL 1.2 and SDL 2.

Changed paths:
    backends/platform/sdl/sdl.cpp



diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index a110a9f..c557531 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -484,8 +484,8 @@ Common::TimerManager *OSystem_SDL::getTimerManager() {
 }
 
 AudioCDManager *OSystem_SDL::createAudioCDManager() {
-	// Audio CD support was removed with SDL 1.3
-#if SDL_VERSION_ATLEAST(1, 3, 0)
+	// Audio CD support was removed with SDL 2.0
+#if SDL_VERSION_ATLEAST(2, 0, 0)
 	return new DefaultAudioCDManager();
 #else
 	return new SdlAudioCDManager();


Commit: 8cd4b881e3e675f5aec8a98f0a1b8b7223a39fba
    https://github.com/scummvm/scummvm/commit/8cd4b881e3e675f5aec8a98f0a1b8b7223a39fba
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2016-03-13T14:16:13+01:00

Commit Message:
BACKENDS: Add ScummVM license header for AudioCD code.

As discussed with clone2727.

Changed paths:
    backends/audiocd/audiocd-stream.cpp
    backends/audiocd/audiocd-stream.h
    backends/audiocd/linux/linux-audiocd.cpp
    backends/audiocd/linux/linux-audiocd.h
    backends/audiocd/macosx/macosx-audiocd.cpp
    backends/audiocd/macosx/macosx-audiocd.h
    backends/audiocd/win32/win32-audiocd.cpp
    backends/audiocd/win32/win32-audiocd.h



diff --git a/backends/audiocd/audiocd-stream.cpp b/backends/audiocd/audiocd-stream.cpp
index 7265b7f..17be5f6 100644
--- a/backends/audiocd/audiocd-stream.cpp
+++ b/backends/audiocd/audiocd-stream.cpp
@@ -1,4 +1,26 @@
-/* Cabal - Legacy Game Implementations
+/* 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
diff --git a/backends/audiocd/audiocd-stream.h b/backends/audiocd/audiocd-stream.h
index e63c060..706c1e9 100644
--- a/backends/audiocd/audiocd-stream.h
+++ b/backends/audiocd/audiocd-stream.h
@@ -1,4 +1,26 @@
-/* Cabal - Legacy Game Implementations
+/* 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
diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp
index b3d355f..f557e00 100644
--- a/backends/audiocd/linux/linux-audiocd.cpp
+++ b/backends/audiocd/linux/linux-audiocd.cpp
@@ -1,4 +1,26 @@
-/* Cabal - Legacy Game Implementations
+/* 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
diff --git a/backends/audiocd/linux/linux-audiocd.h b/backends/audiocd/linux/linux-audiocd.h
index 860afff..69046e9 100644
--- a/backends/audiocd/linux/linux-audiocd.h
+++ b/backends/audiocd/linux/linux-audiocd.h
@@ -1,4 +1,26 @@
-/* Cabal - Legacy Game Implementations
+/* 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
diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp
index d6ff0be..ef5b4c4 100644
--- a/backends/audiocd/macosx/macosx-audiocd.cpp
+++ b/backends/audiocd/macosx/macosx-audiocd.cpp
@@ -1,4 +1,26 @@
-/* Cabal - Legacy Game Implementations
+/* 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
diff --git a/backends/audiocd/macosx/macosx-audiocd.h b/backends/audiocd/macosx/macosx-audiocd.h
index cfa979e..55b8c7b 100644
--- a/backends/audiocd/macosx/macosx-audiocd.h
+++ b/backends/audiocd/macosx/macosx-audiocd.h
@@ -1,4 +1,26 @@
-/* Cabal - Legacy Game Implementations
+/* 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
diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp
index d974746..2fbf9c8 100644
--- a/backends/audiocd/win32/win32-audiocd.cpp
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -1,4 +1,26 @@
-/* Cabal - Legacy Game Implementations
+/* 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
diff --git a/backends/audiocd/win32/win32-audiocd.h b/backends/audiocd/win32/win32-audiocd.h
index d2a5b25..0c10364 100644
--- a/backends/audiocd/win32/win32-audiocd.h
+++ b/backends/audiocd/win32/win32-audiocd.h
@@ -1,4 +1,26 @@
-/* Cabal - Legacy Game Implementations
+/* 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


Commit: 5340df3faf9ee73bc1102ebb5f95c5d82c37df7f
    https://github.com/scummvm/scummvm/commit/5340df3faf9ee73bc1102ebb5f95c5d82c37df7f
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2016-03-13T14:20:20+01:00

Commit Message:
POSIX: Cleanup AudioCD manager creation.

Changed paths:
    backends/platform/sdl/posix/posix.cpp
    backends/platform/sdl/posix/posix.h



diff --git a/backends/platform/sdl/posix/posix.cpp b/backends/platform/sdl/posix/posix.cpp
index 3989bd0..e2a642b 100644
--- a/backends/platform/sdl/posix/posix.cpp
+++ b/backends/platform/sdl/posix/posix.cpp
@@ -243,12 +243,13 @@ bool OSystem_POSIX::displayLogFile() {
 	return WIFEXITED(status) && WEXITSTATUS(status) == 0;
 }
 
-#ifdef USE_LINUXCD
 
 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 9f69bd2..0514d30 100644
--- a/backends/platform/sdl/posix/posix.h
+++ b/backends/platform/sdl/posix/posix.h
@@ -60,10 +60,7 @@ protected:
 
 	virtual Common::WriteStream *createLogFile();
 
-#ifdef USE_LINUXCD
-	// Override createAudioCDManager() for Linux
 	virtual AudioCDManager *createAudioCDManager();
-#endif
 };
 
 #endif


Commit: 5e0b136992c45cbc68d2c8b1e0c61a9e7cce34cb
    https://github.com/scummvm/scummvm/commit/5e0b136992c45cbc68d2c8b1e0c61a9e7cce34cb
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2016-03-13T15:45:19+01:00

Commit Message:
BACKENDS: Fill buffer at start of AudioCD playback.

As suggested by clone2727.

Changed paths:
    backends/audiocd/audiocd-stream.cpp
    backends/audiocd/audiocd-stream.h
    backends/audiocd/linux/linux-audiocd.cpp
    backends/audiocd/win32/win32-audiocd.cpp



diff --git a/backends/audiocd/audiocd-stream.cpp b/backends/audiocd/audiocd-stream.cpp
index 17be5f6..3c0d095 100644
--- a/backends/audiocd/audiocd-stream.cpp
+++ b/backends/audiocd/audiocd-stream.cpp
@@ -170,8 +170,11 @@ void AudioCDStream::onTimer() {
 	}
 }
 
-void AudioCDStream::startTimer() {
+void AudioCDStream::startTimer(bool fillBuffer) {
 	_forceStop = false;
+	if (fillBuffer) {
+		onTimer();
+	}
 	g_system->getTimerManager()->installTimerProc(timerProc, 10 * 1000, this, "AudioCDStream");
 }
 
diff --git a/backends/audiocd/audiocd-stream.h b/backends/audiocd/audiocd-stream.h
index 706c1e9..dbc6a63 100644
--- a/backends/audiocd/audiocd-stream.h
+++ b/backends/audiocd/audiocd-stream.h
@@ -67,7 +67,7 @@ protected:
 	virtual uint getEndFrame() const = 0;
 	virtual bool readFrame(int frame, int16 *buffer) = 0;
 
-	void startTimer();
+	void startTimer(bool fillBuffer = false);
 	void stopTimer();
 
 	enum {
diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp
index f557e00..a868601 100644
--- a/backends/audiocd/linux/linux-audiocd.cpp
+++ b/backends/audiocd/linux/linux-audiocd.cpp
@@ -117,7 +117,9 @@ private:
 
 LinuxAudioCDStream::LinuxAudioCDStream(int fd, const cdrom_tocentry &startEntry, const cdrom_tocentry &endEntry) :
 	_fd(fd), _startEntry(startEntry), _endEntry(endEntry) {
-	startTimer();
+	// 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() {
diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp
index 2fbf9c8..97c45a4 100644
--- a/backends/audiocd/win32/win32-audiocd.cpp
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -96,7 +96,9 @@ private:
 
 Win32AudioCDStream::Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry) :
 	_driveHandle(handle), _startEntry(startEntry), _endEntry(endEntry), _buffer(), _frame(0), _bufferPos(kSamplesPerFrame), _bufferFrame(0) {
-	startTimer();
+	// 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() {


Commit: b9b3d4557c68ebda57a5ac66fcb2ea6a5ed3d92d
    https://github.com/scummvm/scummvm/commit/b9b3d4557c68ebda57a5ac66fcb2ea6a5ed3d92d
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2016-03-13T20:44:04+01:00

Commit Message:
WIN32: Fix AudioCD compilation.

Changed paths:
    backends/audiocd/win32/win32-audiocd.cpp



diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp
index 97c45a4..dc9465e 100644
--- a/backends/audiocd/win32/win32-audiocd.cpp
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -65,20 +65,6 @@
 #include <ddk/ntddcdrm.h>
 #endif
 
-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;
-}
-
 class Win32AudioCDStream : public AudioCDStream {
 public:
 	Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry);
@@ -92,10 +78,24 @@ protected:
 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), _buffer(), _frame(0), _bufferPos(kSamplesPerFrame), _bufferFrame(0) {
+	_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);
@@ -110,7 +110,7 @@ uint Win32AudioCDStream::getStartFrame() const {
 }
 
 uint Win32AudioCDStream::getEndFrame() const {
-	return getFrameCount(_endFrame);
+	return getFrameCount(_endEntry);
 }
 
 bool Win32AudioCDStream::readFrame(int frame, int16 *buffer) {
@@ -141,7 +141,7 @@ public:
 
 	bool open();
 	void close();
-	void play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false);
+	bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false);
 
 protected:
 	bool openCD(int drive);


Commit: 075bde796e73bc169fd5c767db450225d8be0167
    https://github.com/scummvm/scummvm/commit/075bde796e73bc169fd5c767db450225d8be0167
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2016-03-13T20:44:04+01:00

Commit Message:
WIN32: Silence warning in AudioCD code.

Changed paths:
    backends/audiocd/win32/win32-audiocd.cpp



diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp
index dc9465e..6258802 100644
--- a/backends/audiocd/win32/win32-audiocd.cpp
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -42,6 +42,10 @@
  *
  */
 
+#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"
@@ -55,9 +59,6 @@
 #include "common/str.h"
 #include "common/timer.h"
 
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
 #ifdef _MSC_VER
 #include <winioctl.h>
 #include <ntddcdrm.h>


Commit: 045b583141cb0efb0c50aec203fef90a3b912563
    https://github.com/scummvm/scummvm/commit/045b583141cb0efb0c50aec203fef90a3b912563
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2016-03-13T20:44:04+01:00

Commit Message:
WIN32: Guard AudioCD code by WIN32 check.

Changed paths:
    backends/audiocd/win32/win32-audiocd.cpp



diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp
index 6258802..64a7a1b 100644
--- a/backends/audiocd/win32/win32-audiocd.cpp
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -42,6 +42,8 @@
  *
  */
 
+#ifdef WIN32
+
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #undef ARRAYSIZE // winnt.h defines ARRAYSIZE, but we want our own one...
@@ -375,3 +377,5 @@ bool Win32AudioCDManager::tryAddDrive(char drive, DriveList &drives) {
 AudioCDManager *createWin32AudioCDManager() {
 	return new Win32AudioCDManager();
 }
+
+#endif // WIN32
\ No newline at end of file


Commit: 2872c7f9eb3c07dd18c204251a0597a001c0b22c
    https://github.com/scummvm/scummvm/commit/2872c7f9eb3c07dd18c204251a0597a001c0b22c
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2016-03-13T20:44:04+01:00

Commit Message:
WIN32: Fix compilation with MinGW-w64.

Changed paths:
    backends/audiocd/win32/win32-audiocd.cpp



diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp
index 64a7a1b..8037a17 100644
--- a/backends/audiocd/win32/win32-audiocd.cpp
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -61,11 +61,15 @@
 #include "common/str.h"
 #include "common/timer.h"
 
-#ifdef _MSC_VER
+#if defined(_MSC_VER)
 #include <winioctl.h>
 #include <ntddcdrm.h>
-#else
+#elif defined(__MINGW32__) && !defined(__MINGW64__)
+// Classic MinGW uses non standard paths for DDK headers.
 #include <ddk/ntddcdrm.h>
+#else
+#include <winioctl.h>
+#include <ntddcdrm.h>
 #endif
 
 class Win32AudioCDStream : public AudioCDStream {


Commit: 88f8ecde0b3e29a6901b408de535216f70357028
    https://github.com/scummvm/scummvm/commit/88f8ecde0b3e29a6901b408de535216f70357028
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2016-03-14T01:03:13+01:00

Commit Message:
WIN32: Supply required DDK headers for older MSVC.

Header file taken from MinGW's w32api 4.0.3.

Changed paths:
  A backends/audiocd/win32/msvc/ntddcdrm.h
    backends/audiocd/win32/win32-audiocd.cpp



diff --git a/backends/audiocd/win32/msvc/ntddcdrm.h b/backends/audiocd/win32/msvc/ntddcdrm.h
new file mode 100644
index 0000000..18527e2
--- /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 at 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
index 8037a17..eb9c472 100644
--- a/backends/audiocd/win32/win32-audiocd.cpp
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -61,9 +61,13 @@
 #include "common/str.h"
 #include "common/timer.h"
 
-#if defined(_MSC_VER)
+#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 <winioctl.h>
-#include <ntddcdrm.h>
+#include "msvc/ntddcdrm.h"
 #elif defined(__MINGW32__) && !defined(__MINGW64__)
 // Classic MinGW uses non standard paths for DDK headers.
 #include <ddk/ntddcdrm.h>


Commit: 6a127df564a9e02d154b8e8f6e930896e7199349
    https://github.com/scummvm/scummvm/commit/6a127df564a9e02d154b8e8f6e930896e7199349
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2016-03-14T01:04:11+01:00

Commit Message:
WIN32: Cleanup header includes for AudioCD code.

Changed paths:
    backends/audiocd/win32/win32-audiocd.cpp



diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp
index eb9c472..6c057ef 100644
--- a/backends/audiocd/win32/win32-audiocd.cpp
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -61,18 +61,17 @@
 #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 <winioctl.h>
 #include "msvc/ntddcdrm.h"
 #elif defined(__MINGW32__) && !defined(__MINGW64__)
 // Classic MinGW uses non standard paths for DDK headers.
 #include <ddk/ntddcdrm.h>
 #else
-#include <winioctl.h>
 #include <ntddcdrm.h>
 #endif
 


Commit: ad678cf083430e92729ede882fcfa2ff0fa5a2b0
    https://github.com/scummvm/scummvm/commit/ad678cf083430e92729ede882fcfa2ff0fa5a2b0
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2016-03-15T14:24:23+01:00

Commit Message:
MACOSX: Replace manual uint parsing by strtol.

Changed paths:
    backends/audiocd/macosx/macosx-audiocd.cpp



diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp
index ef5b4c4..56075f2 100644
--- a/backends/audiocd/macosx/macosx-audiocd.cpp
+++ b/backends/audiocd/macosx/macosx-audiocd.cpp
@@ -44,6 +44,7 @@
 
 #include <sys/stat.h>
 #include <sys/mount.h>
+#include <limits.h>
 
 #include "common/scummsys.h"
 
@@ -274,15 +275,20 @@ bool MacOSXAudioCDManager::findTrackNames(const Common::String &drivePath) {
 			Common::String fileName = children[i].getName();
 
 			if (fileName.hasSuffix(".aiff") || fileName.hasSuffix(".cdda")) {
-				uint trackID = 0, j = 0;
+				uint j = 0;
 
 				for (; j < fileName.size() && !Common::isDigit(fileName[j]); j++)
 					;
 
-				for (; j < fileName.size() && Common::isDigit(fileName[j]); j++)
-					trackID = trackID * 10 + (fileName[j] - '0');
+				const char *trackIDString = fileName.c_str() + j;
+				char *endPtr = nullptr;
+				long trackID = strtol(trackIDString, &endPtr, 10);
 
-				_trackMap[trackID - 1] = drivePath + '/' + fileName;
+				if (trackIDString != endPtr && trackID > 0 && trackID < UINT_MAX) {
+					_trackMap[trackID - 1] = drivePath + '/' + fileName;
+				} else {
+					warning("Invalid track file name: '%s'", fileName.c_str());
+				}
 			}
 		}
 	}


Commit: f75bc481415c0b3e56627aaa84c6151a62c789b5
    https://github.com/scummvm/scummvm/commit/f75bc481415c0b3e56627aaa84c6151a62c789b5
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2016-03-15T14:24:23+01:00

Commit Message:
MACOSX: Guard audio cd code by define check.

Changed paths:
    backends/audiocd/macosx/macosx-audiocd.cpp



diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp
index 56075f2..d03541a 100644
--- a/backends/audiocd/macosx/macosx-audiocd.cpp
+++ b/backends/audiocd/macosx/macosx-audiocd.cpp
@@ -42,6 +42,8 @@
  *
  */
 
+#ifdef MACOSX
+
 #include <sys/stat.h>
 #include <sys/mount.h>
 #include <limits.h>
@@ -299,3 +301,5 @@ bool MacOSXAudioCDManager::findTrackNames(const Common::String &drivePath) {
 AudioCDManager *createMacOSXAudioCDManager() {
 	return new MacOSXAudioCDManager();
 }
+
+#endif // MACOSX


Commit: 7725e00e9a10f5bb93ebad2c1befdc448e0d945c
    https://github.com/scummvm/scummvm/commit/7725e00e9a10f5bb93ebad2c1befdc448e0d945c
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2016-03-15T14:24:23+01:00

Commit Message:
LINUX: Guard Linux audio CD code.

Changed paths:
    backends/audiocd/linux/linux-audiocd.cpp
    backends/audiocd/linux/linux-audiocd.h



diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp
index a868601..a5838e1 100644
--- a/backends/audiocd/linux/linux-audiocd.cpp
+++ b/backends/audiocd/linux/linux-audiocd.cpp
@@ -42,15 +42,13 @@
  *
  */
 
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <linux/cdrom.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
+// 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"
@@ -58,6 +56,13 @@
 #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
 };
@@ -460,3 +465,5 @@ bool LinuxAudioCDManager::hasDevice(const DeviceList &devices, dev_t device) {
 AudioCDManager *createLinuxAudioCDManager() {
 	return new LinuxAudioCDManager();
 }
+
+#endif // USE_LINUXCD
diff --git a/backends/audiocd/linux/linux-audiocd.h b/backends/audiocd/linux/linux-audiocd.h
index 69046e9..09d6353 100644
--- a/backends/audiocd/linux/linux-audiocd.h
+++ b/backends/audiocd/linux/linux-audiocd.h
@@ -45,6 +45,8 @@
 #ifndef BACKENDS_AUDIOCD_LINUX_H
 #define BACKENDS_AUDIOCD_LINUX_H
 
+#include "common/scummsys.h"
+
 #ifdef USE_LINUXCD
 
 class AudioCDManager;


Commit: afe8ea139ccc4e563260494c236642153c540001
    https://github.com/scummvm/scummvm/commit/afe8ea139ccc4e563260494c236642153c540001
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2016-03-15T14:25:42+01:00

Commit Message:
MACOSX: Add comment about track ID search loop.

Changed paths:
    backends/audiocd/macosx/macosx-audiocd.cpp



diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp
index d03541a..e8d41c3 100644
--- a/backends/audiocd/macosx/macosx-audiocd.cpp
+++ b/backends/audiocd/macosx/macosx-audiocd.cpp
@@ -279,6 +279,7 @@ bool MacOSXAudioCDManager::findTrackNames(const Common::String &drivePath) {
 			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++)
 					;
 


Commit: 5336882555e4834bb5b93fddcf4231d3def9d028
    https://github.com/scummvm/scummvm/commit/5336882555e4834bb5b93fddcf4231d3def9d028
Author: Johannes Schickel (lordhoto at gmail.com)
Date: 2016-03-21T01:48:38+01:00

Commit Message:
Merge pull request #704 from lordhoto/clone2727-audiocd

Implement Custom Audio CD Support for Win32, Linux, and Mac OS X.

Changed paths:
  A backends/audiocd/audiocd-stream.cpp
  A backends/audiocd/audiocd-stream.h
  A backends/audiocd/linux/linux-audiocd.cpp
  A backends/audiocd/linux/linux-audiocd.h
  A backends/audiocd/macosx/macosx-audiocd.cpp
  A backends/audiocd/macosx/macosx-audiocd.h
  A backends/audiocd/win32/msvc/ntddcdrm.h
  A backends/audiocd/win32/win32-audiocd.cpp
  A backends/audiocd/win32/win32-audiocd.h
    backends/audiocd/audiocd.h
    backends/audiocd/default/default-audiocd.cpp
    backends/audiocd/default/default-audiocd.h
    backends/audiocd/sdl/sdl-audiocd.cpp
    backends/audiocd/sdl/sdl-audiocd.h
    backends/module.mk
    backends/platform/dc/dc.h
    backends/platform/dc/dcmain.cpp
    backends/platform/ds/arm9/source/osystem_ds.cpp
    backends/platform/ds/arm9/source/osystem_ds.h
    backends/platform/sdl/macosx/macosx.cpp
    backends/platform/sdl/macosx/macosx.h
    backends/platform/sdl/posix/posix.cpp
    backends/platform/sdl/posix/posix.h
    backends/platform/sdl/sdl.cpp
    backends/platform/sdl/sdl.h
    backends/platform/sdl/win32/win32.cpp
    backends/platform/sdl/win32/win32.h
    base/commandLine.cpp
    configure
    engines/agos/event.cpp
    engines/agos/res_snd.cpp
    engines/cine/main_loop.cpp
    engines/cine/sound.cpp
    engines/drascula/drascula.cpp
    engines/drascula/sound.cpp
    engines/gob/gob.cpp
    engines/groovie/groovie.cpp
    engines/kyra/sound_towns.cpp
    engines/made/made.cpp
    engines/sci/sound/audio.cpp
    engines/sci/sound/audio.h
    engines/scumm/scumm.cpp
    engines/scumm/sound.cpp
    engines/teenagent/teenagent.cpp
    engines/tinsel/tinsel.cpp









More information about the Scummvm-git-logs mailing list