From fb6fe1332aaa9ab4d0ec0ad4ee8015ce4076bd85 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Tue, 29 Sep 2015 21:00:56 -0400 Subject: [PATCH 01/36] BACKENDS: Move to an openCD() without parameters as the public API --- backends/audiocd/audiocd.h | 3 +-- backends/audiocd/default/default-audiocd.cpp | 5 +++++ backends/audiocd/default/default-audiocd.h | 7 +++++++ backends/platform/dc/dc.h | 2 +- backends/platform/dc/dcmain.cpp | 2 +- backends/platform/ds/arm9/source/osystem_ds.cpp | 2 +- backends/platform/ds/arm9/source/osystem_ds.h | 2 +- engines/drascula/drascula.cpp | 4 +--- engines/gob/gob.cpp | 4 +--- engines/groovie/groovie.cpp | 6 +----- engines/made/made.cpp | 4 +--- engines/scumm/scumm.cpp | 5 +---- engines/tinsel/tinsel.cpp | 4 +--- 13 files changed, 23 insertions(+), 27 deletions(-) diff --git a/backends/audiocd/audiocd.h b/backends/audiocd/audiocd.h index 6eae8e096b1..72a01fd87e8 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 abf80ac4cd9..0c5bb8df039 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 9e4ba6b33e3..1d64690addd 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 d8ab549c3a6..80e484f6f8a 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 eede796991c..aa8430afc88 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 c53f57523da..f23192cd9db 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 f4dbac66f7c..9b66e24b88d 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 c72d77c2813..1a7659fc85a 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 24bdb858d88..df2d804bd26 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 2021cef6e86..a25bf0008b7 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 ab07ef757bc..57130e277f9 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 89d2d3dc72c..680bdb24636 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 2adddca4fd9..782dd7ae48b 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; From 016a6000e425bf4b5b696eae93316afbba15eb79 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Tue, 29 Sep 2015 01:06:17 -0400 Subject: [PATCH 02/36] CINE: Ensure openCD() is called --- engines/cine/sound.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp index 7cab0673715..0e7da2e2bce 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() { From c0e11a431dd5c4a82ab754c3f94bb95ee46de792 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Tue, 29 Sep 2015 01:11:17 -0400 Subject: [PATCH 03/36] SCI: Ensure openCD() is called --- engines/sci/sound/audio.cpp | 9 ++++++++- engines/sci/sound/audio.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp index 57f04152858..5e5e8b0466b 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 9e65d6e0c87..4a8b26567d2 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 From e9441aa809634900ad4cbd414510f057f62329f2 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Tue, 29 Sep 2015 01:13:05 -0400 Subject: [PATCH 04/36] TEENAGENT: Ensure openCD() is called --- engines/teenagent/teenagent.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/engines/teenagent/teenagent.cpp b/engines/teenagent/teenagent.cpp index d5a8b8e2dc2..f5d9f72cc32 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); From fc8480c84582d83556fc70f8a8fed004bbe9f766 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Tue, 29 Sep 2015 01:19:09 -0400 Subject: [PATCH 05/36] KYRA: Ensure openCD() is called --- engines/kyra/sound_towns.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp index 65ab4f31ef7..b269cb4d672 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 From ec5df573cbd8ea33e0ee34f27cc7d732485f3ed1 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Tue, 29 Sep 2015 23:57:25 -0400 Subject: [PATCH 06/36] AGOS: Mark the audio CD playback as emulate only --- engines/agos/res_snd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engines/agos/res_snd.cpp b/engines/agos/res_snd.cpp index d04f1735d6a..3a092e652cf 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; From 30e68efac41a96e97fc9d42d0b7f9903e76ccfb5 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Wed, 30 Sep 2015 00:25:24 -0400 Subject: [PATCH 07/36] BACKENDS: Add a closeCD() function to the AudioCDManager --- backends/audiocd/audiocd.h | 5 +++++ backends/audiocd/default/default-audiocd.h | 1 + backends/audiocd/sdl/sdl-audiocd.cpp | 13 +++++++++---- backends/audiocd/sdl/sdl-audiocd.h | 1 + backends/platform/dc/dc.h | 3 +++ backends/platform/ds/arm9/source/osystem_ds.h | 1 + 6 files changed, 20 insertions(+), 4 deletions(-) diff --git a/backends/audiocd/audiocd.h b/backends/audiocd/audiocd.h index 72a01fd87e8..598893e3156 100644 --- a/backends/audiocd/audiocd.h +++ b/backends/audiocd/audiocd.h @@ -113,6 +113,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 1d64690addd..6011a8fafd3 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 ff50c56af3c..d745f29a123 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 bfad7b68056..4ece8ddfe7f 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 80e484f6f8a..b49080324d8 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 9b66e24b88d..9f73e125c20 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(); From 55a87c59b61fe6758faee3a3e388b2a8979c7cb8 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Sat, 3 Oct 2015 14:07:30 -0400 Subject: [PATCH 08/36] BACKENDS: Use a virtual function for creating the SDL audio CD manager --- backends/platform/sdl/sdl.cpp | 19 ++++++++++--------- backends/platform/sdl/sdl.h | 5 +++++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index fffb9d56d99..a110a9ff2f3 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 5ee56d0568e..c93c8308a79 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; From 47a82f2d1b776ec46d98715c9af3e7ec37a3d206 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Sun, 30 Dec 2012 13:38:56 -0500 Subject: [PATCH 09/36] 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. --- backends/audiocd/macosx/macosx-audiocd.cpp | 239 +++++++++++++++++++++ backends/audiocd/macosx/macosx-audiocd.h | 39 ++++ backends/module.mk | 1 + backends/platform/sdl/macosx/macosx.cpp | 7 +- backends/platform/sdl/macosx/macosx.h | 5 + 5 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 backends/audiocd/macosx/macosx-audiocd.cpp create mode 100644 backends/audiocd/macosx/macosx-audiocd.h diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp new file mode 100644 index 00000000000..a8ba712621e --- /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 +#include + +#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 DriveList; + DriveList detectAllDrives(); + DriveList detectCDDADrives(); + + bool findTrackNames(const Common::String &drivePath); + + Common::HashMap _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(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 00000000000..cfa979e2c27 --- /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 3d412c031ad..01e601ead7c 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 38a2d7441c3..7652c0d8332 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 c8b4beaeeca..6905284a5fb 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 From a1a4fc0d5186ae4aeeb1e5ea99be4b36bf645179 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Sun, 27 Sep 2015 18:02:21 -0400 Subject: [PATCH 10/36] BACKENDS: Add a Linux CD-ROM audio player --- backends/audiocd/linux/linux-audiocd.cpp | 350 +++++++++++++++++++++++ backends/audiocd/linux/linux-audiocd.h | 38 +++ backends/module.mk | 5 + backends/platform/sdl/posix/posix.cpp | 12 + backends/platform/sdl/posix/posix.h | 5 + configure | 14 + 6 files changed, 424 insertions(+) create mode 100644 backends/audiocd/linux/linux-audiocd.cpp create mode 100644 backends/audiocd/linux/linux-audiocd.h diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp new file mode 100644 index 00000000000..a173a9d3be5 --- /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 +#include +#include +#include +#include +#include + +#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 scanDevices(); + bool tryAddDrive(const Common::String &drive, Common::Array &devices); + bool loadTOC(); + + int _fd; + cdrom_tochdr _tocHeader; + Common::Array _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 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::scanDevices() { + Common::Array devices; + tryAddDrive("/dev/cdrom", devices); + return devices; +} + +bool LinuxAudioCDManager::tryAddDrive(const Common::String &drive, Common::Array &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 00000000000..860afffa87f --- /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 01e601ead7c..40b63e2aa4a 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 525c74a91ab..3989bd0043b 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 #include #include #include @@ -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 f67515ddb35..9f69bd227f9 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 0e7a5a9b567..c958e9acf03 100755 --- a/configure +++ b/configure @@ -4258,6 +4258,20 @@ fi 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 +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 # From 91d695d4a2fea6742c31abd246a22c40797b014f Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Mon, 28 Sep 2015 21:13:54 -0400 Subject: [PATCH 11/36] BACKENDS: Detect the CD drive from the game's path on Linux --- backends/audiocd/linux/linux-audiocd.cpp | 77 ++++++++++++++++++++---- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp index a173a9d3be5..c079c2d7446 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 scanDevices(); - bool tryAddDrive(const Common::String &drive, Common::Array &devices); + typedef Common::Array 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 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::scanDevices() { - Common::Array 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 &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(); } From 2ee85fcbd9a457d041436e4cdb600a2171ad0b1c Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Mon, 28 Sep 2015 21:22:01 -0400 Subject: [PATCH 12/36] BACKENDS: Use a threading for buffering Linux CD audio --- backends/audiocd/linux/linux-audiocd.cpp | 146 +++++++++++++++++++---- 1 file changed, 125 insertions(+), 21 deletions(-) diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp index c079c2d7446..917546cfa53 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 _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(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 { From 2aa2a6b56cd011673e0899a48daac5593e4f198f Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Tue, 29 Sep 2015 17:47:57 -0400 Subject: [PATCH 13/36] BACKENDS: Add a Win32 audio CD player --- backends/audiocd/win32/win32-audiocd.cpp | 363 +++++++++++++++++++++++ backends/audiocd/win32/win32-audiocd.h | 38 +++ backends/module.mk | 1 + backends/platform/sdl/win32/win32.cpp | 5 + backends/platform/sdl/win32/win32.h | 4 + 5 files changed, 411 insertions(+) create mode 100644 backends/audiocd/win32/win32-audiocd.cpp create mode 100644 backends/audiocd/win32/win32-audiocd.h diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp new file mode 100644 index 00000000000..fdde61cc456 --- /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 + +#ifdef _MSC_VER +#include +#include +#else +#include +#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 DriveList; + DriveList detectDrives(); + bool tryAddDrive(char drive, DriveList &drives); + + HANDLE _driveHandle; + int _firstTrack, _lastTrack; + Common::Array _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 00000000000..d2a5b257a7f --- /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 40b63e2aa4a..8abab65f143 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 0f70c00b407..fbab7eb7824 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 473e78ff0b1..ca0843e8347 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 From e39faa239a8b0149d3be37e21afb173b936ab74c Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Tue, 29 Sep 2015 20:15:52 -0400 Subject: [PATCH 14/36] BACKENDS: Use a second thread to assist in buffering Win32 CD audio --- backends/audiocd/win32/win32-audiocd.cpp | 152 ++++++++++++++++++----- 1 file changed, 121 insertions(+), 31 deletions(-) diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp index fdde61cc456..6dd2b756a94 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 @@ -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 _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); + // Fetch a frame from the queue + int16 *buffer; - // 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; + { + 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(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(); From 1626fbd633ff5e39ee5551b6e839def62ef9b599 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Thu, 1 Oct 2015 19:12:07 -0400 Subject: [PATCH 15/36] BACKENDS: Allow for specifying a drive via the cdrom option --- backends/audiocd/default/default-audiocd.cpp | 16 +++++++++++++++- backends/audiocd/default/default-audiocd.h | 12 ++++++++++++ base/commandLine.cpp | 5 +++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/backends/audiocd/default/default-audiocd.cpp b/backends/audiocd/default/default-audiocd.cpp index 0c5bb8df039..4c08938741f 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 6011a8fafd3..5c7ee9ee34f 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 19702ea36de..c34e4d43c05 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" From 442f91c622075c026c1c2295d72d7f4b7e06b665 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Thu, 1 Oct 2015 19:12:37 -0400 Subject: [PATCH 16/36] BACKENDS: Add support for opening a CD on Linux by path or drive --- backends/audiocd/linux/linux-audiocd.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp index 917546cfa53..fe3ae1e58b5 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; From 4a6c7b5c831f6d054033d9ded5cb83e4eb6f8d56 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Thu, 1 Oct 2015 20:36:01 -0400 Subject: [PATCH 17/36] BACKENDS: Add support for opening a CD on Windows by drive --- backends/audiocd/win32/win32-audiocd.cpp | 34 ++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp index 6dd2b756a94..5de7c7b2e60 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(); From dc0d4fcf303458e9044866dd05a30c317e07eef0 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Sat, 3 Oct 2015 13:18:18 -0400 Subject: [PATCH 18/36] BACKENDS: Add support for opening a CD on Mac OS X by path or drive --- backends/audiocd/macosx/macosx-audiocd.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp index a8ba712621e..76bae955005 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(); From aa6ff444408bfd17bcca1d8364e86ce62da327ef Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Tue, 6 Oct 2015 22:10:34 -0400 Subject: [PATCH 19/36] 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. --- backends/audiocd/audiocd.h | 81 +++++--------------- backends/audiocd/default/default-audiocd.cpp | 76 ++++++++---------- backends/audiocd/default/default-audiocd.h | 29 ++++--- backends/audiocd/linux/linux-audiocd.cpp | 68 +++++++++------- backends/audiocd/macosx/macosx-audiocd.cpp | 45 +++++++---- backends/audiocd/sdl/sdl-audiocd.cpp | 54 +++++++++---- backends/audiocd/sdl/sdl-audiocd.h | 12 +-- backends/audiocd/win32/win32-audiocd.cpp | 47 ++++++++---- backends/platform/dc/dc.h | 24 ++---- backends/platform/dc/dcmain.cpp | 62 +++++++-------- engines/agos/event.cpp | 4 +- engines/cine/main_loop.cpp | 2 +- engines/cine/sound.cpp | 2 +- engines/drascula/drascula.cpp | 2 +- engines/drascula/sound.cpp | 2 +- engines/gob/gob.cpp | 2 +- engines/groovie/groovie.cpp | 2 +- engines/kyra/sound_towns.cpp | 20 ++--- engines/made/made.cpp | 4 +- engines/sci/sound/audio.cpp | 2 +- engines/scumm/scumm.cpp | 2 +- engines/scumm/sound.cpp | 2 +- engines/teenagent/teenagent.cpp | 2 +- engines/tinsel/tinsel.cpp | 4 +- 24 files changed, 282 insertions(+), 268 deletions(-) diff --git a/backends/audiocd/audiocd.h b/backends/audiocd/audiocd.h index 598893e3156..b3674f25700 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 4c08938741f..c2ce7cedccc 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 5c7ee9ee34f..e3fbb4b5a19 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 fe3ae1e58b5..a835ade2189 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::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) { - closeCD(); - 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; +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; - 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(); + // 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 76bae955005..07d8d31f264 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(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 d745f29a123..3558fb56713 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 4ece8ddfe7f..91895dac99c 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 5de7c7b2e60..e3fdcf64778 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::open() { + close(); + + if (openRealCD()) + return true; + + return DefaultAudioCDManager::open(); } bool Win32AudioCDManager::openCD(int drive) { - closeCD(); - // 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 b49080324d8..b567142b8fc 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(); +public: + // Poll cdrom status + // Returns true if cd audio is playing + bool isPlaying() const; - // Close the open CD drive - void closeCD() {} + // Play cdrom audio track + void play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false); - // 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); - - // Stop cdrom audio track - void stopCD(); - - // 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 aa8430afc88..bd66b81b356 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); + + if (numLoops > 14) + numLoops = 14; + else if (numLoops < 0) + num_loops = 15; // infinity + + if (!find_track(track, firstSec, lastSec)) + return; + + if (duration) + lastSec = firstSec + startFrame + duration; + + firstSec += startFrame; + play_cdda_sectors(firstSec, lastSec, numLoops); } -void DCCDManager::stopCD() -{ - stop_cdda(); +void DCCDManager::stop() { + DefaultAudioCDManager::stop(); + stop_cdda(); } -bool DCCDManager::pollCD() -{ - extern int getCdState(); - return getCdState() == 3; -} +bool DCCDManager::isPlaying() const { + if (DefaultAudioCDManager::isPlaying()) + return true; -void DCCDManager::updateCD() -{ - // Dummy. The CD drive takes care of itself. -} - -bool DCCDManager::openCD() -{ - // Dummy. - 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 95bcc682346..5240cdd7713 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 e52fc464d53..19a2d8a82e6 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 0e7da2e2bce..a8b4c085ff5 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 1a7659fc85a..9ac9031fb72 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 148dae76f59..c576b376603 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 df2d804bd26..d995f26d9f0 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 a25bf0008b7..bbc290eccfb 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 b269cb4d672..646f908b94a 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 57130e277f9..f1539297ee3 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 5e5e8b0466b..a74bfa245ff 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 680bdb24636..d9148ed3004 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 4d70ee8482f..404bdd022c2 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 f5d9f72cc32..4dc785754c2 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 782dd7ae48b..1b733a08ba5 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(); } From 41a1dcb0bb8bd7aaa20d03ea92aa49210b66fa92 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Fri, 16 Oct 2015 08:36:16 -0400 Subject: [PATCH 20/36] BACKENDS: Switch to a common base class for threaded audio CD streams --- backends/audiocd/audiocd-stream.cpp | 174 +++++++++++++++++++ backends/audiocd/audiocd-stream.h | 86 ++++++++++ backends/audiocd/linux/linux-audiocd.cpp | 181 +++----------------- backends/audiocd/win32/win32-audiocd.cpp | 204 ++++------------------- backends/module.mk | 1 + 5 files changed, 315 insertions(+), 331 deletions(-) create mode 100644 backends/audiocd/audiocd-stream.cpp create mode 100644 backends/audiocd/audiocd-stream.h diff --git a/backends/audiocd/audiocd-stream.cpp b/backends/audiocd/audiocd-stream.cpp new file mode 100644 index 00000000000..da55654a1d9 --- /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(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 00000000000..a8938025b6a --- /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 _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 a835ade2189..ea5eb1f9be1 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 _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; +bool LinuxAudioCDStream::readFrame(int frame, int16 *buffer) { + int seconds = frame / kFramesPerSecond; + frame %= kFramesPerSecond; + int minutes = seconds / kSecondsPerMinute; + seconds %= kSecondsPerMinute; - // See if any data is left first - while (_bufferPos < kSamplesPerFrame && samples < numSamples) - buffer[samples++] = _buffer[_bufferPos++]; + // 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); - // Bail out if done - if (endOfData()) - return samples; + if (ioctl(_fd, CDROMREADAUDIO, &readAudio) < 0) + return false; - 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; +uint LinuxAudioCDStream::getStartFrame() const { + return getFrameCount(_startEntry.cdte_addr.msf); } -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; +uint LinuxAudioCDStream::getEndFrame() const { + return getFrameCount(_endEntry.cdte_addr.msf); } -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(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 = _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 e3fdcf64778..0b04a7ca5da 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" @@ -42,30 +43,11 @@ #include #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 _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; +uint Win32AudioCDStream::getStartFrame() const { + return getFrameCount(_startEntry); } -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; +uint Win32AudioCDStream::getEndFrame() const { + return getFrameCount(_endFrame); } -bool Win32AudioCDStream::endOfData() const { - return getFrameCount(_startEntry) + _frame >= getFrameCount(_endEntry) && _bufferPos == kSamplesPerFrame; +bool Win32AudioCDStream::readFrame(int frame, int16 *buffer) { + // Request to read that frame + RAW_READ_INFO readAudio; + memset(&readAudio, 0, sizeof(readAudio)); + readAudio.DiskOffset.QuadPart = (frame - kPreGapFrames) * 2048; + readAudio.SectorCount = 1; + readAudio.TrackMode = CDDA; + + DWORD bytesReturned; + return DeviceIoControl( + _driveHandle, + IOCTL_CDROM_RAW_READ, + &readAudio, + sizeof(readAudio), + buffer, + kBytesPerFrame, + &bytesReturned, + NULL); } -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); -} - -void Win32AudioCDStream::timerProc(void *refCon) { - static_cast(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: diff --git a/backends/module.mk b/backends/module.mk index 8abab65f143..3f7620e2a8c 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 \ From c6c0f527a7394d70afd4a50d8b9b41d035bbe7b8 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Mon, 19 Oct 2015 22:14:50 -0400 Subject: [PATCH 21/36] BACKENDS: Switch to CDROMREADRAW for Linux audio CD CDROMREADAUDIO seems to crash the kernel when ejecting while playing. Let's try to avoid that. --- backends/audiocd/linux/linux-audiocd.cpp | 40 +++++++++++++++++++----- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp index ea5eb1f9be1..b48ce78b94a 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); + // 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, CDROMREADAUDIO, &readAudio) < 0) + 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; } From 90899860b4e4843cc42b272c86d15951c8846dca Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 13 Mar 2016 14:06:20 +0100 Subject: [PATCH 22/36] BACKENDS: Formatting fixes. --- backends/audiocd/audiocd-stream.cpp | 2 +- backends/audiocd/audiocd-stream.h | 18 +++---- backends/audiocd/linux/linux-audiocd.cpp | 20 ++++---- backends/audiocd/macosx/macosx-audiocd.cpp | 2 +- backends/audiocd/win32/win32-audiocd.cpp | 58 +++++++++++----------- 5 files changed, 50 insertions(+), 50 deletions(-) diff --git a/backends/audiocd/audiocd-stream.cpp b/backends/audiocd/audiocd-stream.cpp index da55654a1d9..7265b7f1350 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 a8938025b6a..e63c060c549 100644 --- a/backends/audiocd/audiocd-stream.h +++ b/backends/audiocd/audiocd-stream.h @@ -70,17 +70,17 @@ private: Common::Queue _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 b48ce78b94a..b3d355fecd2 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 07d8d31f264..d6ff0be99bc 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 0b04a7ca5da..d9747467c78 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 From b32f347531cf9aa5b23db5ce875b5b7a295022bb Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 13 Mar 2016 14:07:10 +0100 Subject: [PATCH 23/36] SDL: Get rid of reference to SDL 1.3. We only support SDL 1.2 and SDL 2. --- backends/platform/sdl/sdl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index a110a9ff2f3..c55753194b1 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(); From 8cd4b881e3e675f5aec8a98f0a1b8b7223a39fba Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 13 Mar 2016 14:16:11 +0100 Subject: [PATCH 24/36] BACKENDS: Add ScummVM license header for AudioCD code. As discussed with clone2727. --- backends/audiocd/audiocd-stream.cpp | 24 +++++++++++++++++++++- backends/audiocd/audiocd-stream.h | 24 +++++++++++++++++++++- backends/audiocd/linux/linux-audiocd.cpp | 24 +++++++++++++++++++++- backends/audiocd/linux/linux-audiocd.h | 24 +++++++++++++++++++++- backends/audiocd/macosx/macosx-audiocd.cpp | 24 +++++++++++++++++++++- backends/audiocd/macosx/macosx-audiocd.h | 24 +++++++++++++++++++++- backends/audiocd/win32/win32-audiocd.cpp | 24 +++++++++++++++++++++- backends/audiocd/win32/win32-audiocd.h | 24 +++++++++++++++++++++- 8 files changed, 184 insertions(+), 8 deletions(-) diff --git a/backends/audiocd/audiocd-stream.cpp b/backends/audiocd/audiocd-stream.cpp index 7265b7f1350..17be5f6dc6e 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 e63c060c549..706c1e90958 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 b3d355fecd2..f557e00d6d4 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 860afffa87f..69046e936a5 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 d6ff0be99bc..ef5b4c453a8 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 cfa979e2c27..55b8c7b8c66 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 d9747467c78..2fbf9c88104 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 d2a5b257a7f..0c103641efc 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 From 5340df3faf9ee73bc1102ebb5f95c5d82c37df7f Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 13 Mar 2016 14:20:20 +0100 Subject: [PATCH 25/36] POSIX: Cleanup AudioCD manager creation. --- backends/platform/sdl/posix/posix.cpp | 7 ++++--- backends/platform/sdl/posix/posix.h | 3 --- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/backends/platform/sdl/posix/posix.cpp b/backends/platform/sdl/posix/posix.cpp index 3989bd0043b..e2a642b2888 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 - -#endif diff --git a/backends/platform/sdl/posix/posix.h b/backends/platform/sdl/posix/posix.h index 9f69bd227f9..0514d30191a 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 From 5e0b136992c45cbc68d2c8b1e0c61a9e7cce34cb Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 13 Mar 2016 14:30:36 +0100 Subject: [PATCH 26/36] BACKENDS: Fill buffer at start of AudioCD playback. As suggested by clone2727. --- backends/audiocd/audiocd-stream.cpp | 5 ++++- backends/audiocd/audiocd-stream.h | 2 +- backends/audiocd/linux/linux-audiocd.cpp | 4 +++- backends/audiocd/win32/win32-audiocd.cpp | 4 +++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/backends/audiocd/audiocd-stream.cpp b/backends/audiocd/audiocd-stream.cpp index 17be5f6dc6e..3c0d0957da2 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 706c1e90958..dbc6a6321b8 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 f557e00d6d4..a86860106a1 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 2fbf9c88104..97c45a49469 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() { From b9b3d4557c68ebda57a5ac66fcb2ea6a5ed3d92d Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 13 Mar 2016 19:17:28 +0100 Subject: [PATCH 27/36] WIN32: Fix AudioCD compilation. --- backends/audiocd/win32/win32-audiocd.cpp | 34 ++++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp index 97c45a49469..dc9465e1856 100644 --- a/backends/audiocd/win32/win32-audiocd.cpp +++ b/backends/audiocd/win32/win32-audiocd.cpp @@ -65,20 +65,6 @@ #include #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); From 075bde796e73bc169fd5c767db450225d8be0167 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 13 Mar 2016 19:17:40 +0100 Subject: [PATCH 28/36] WIN32: Silence warning in AudioCD code. --- backends/audiocd/win32/win32-audiocd.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp index dc9465e1856..62588023779 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 +#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 - #ifdef _MSC_VER #include #include From 045b583141cb0efb0c50aec203fef90a3b912563 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 13 Mar 2016 19:18:39 +0100 Subject: [PATCH 29/36] WIN32: Guard AudioCD code by WIN32 check. --- backends/audiocd/win32/win32-audiocd.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp index 62588023779..64a7a1b9ec3 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 #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 From 2872c7f9eb3c07dd18c204251a0597a001c0b22c Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 13 Mar 2016 19:43:04 +0100 Subject: [PATCH 30/36] WIN32: Fix compilation with MinGW-w64. --- backends/audiocd/win32/win32-audiocd.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp index 64a7a1b9ec3..8037a179a47 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 #include -#else +#elif defined(__MINGW32__) && !defined(__MINGW64__) +// Classic MinGW uses non standard paths for DDK headers. #include +#else +#include +#include #endif class Win32AudioCDStream : public AudioCDStream { From 88f8ecde0b3e29a6901b408de535216f70357028 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 13 Mar 2016 20:02:34 +0100 Subject: [PATCH 31/36] WIN32: Supply required DDK headers for older MSVC. Header file taken from MinGW's w32api 4.0.3. --- backends/audiocd/win32/msvc/ntddcdrm.h | 362 +++++++++++++++++++++++ backends/audiocd/win32/win32-audiocd.cpp | 8 +- 2 files changed, 368 insertions(+), 2 deletions(-) create mode 100644 backends/audiocd/win32/msvc/ntddcdrm.h diff --git a/backends/audiocd/win32/msvc/ntddcdrm.h b/backends/audiocd/win32/msvc/ntddcdrm.h new file mode 100644 index 00000000000..18527e26759 --- /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 */ +#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 8037a179a47..eb9c472ff91 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 -#include +#include "msvc/ntddcdrm.h" #elif defined(__MINGW32__) && !defined(__MINGW64__) // Classic MinGW uses non standard paths for DDK headers. #include From 6a127df564a9e02d154b8e8f6e930896e7199349 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 13 Mar 2016 20:06:51 +0100 Subject: [PATCH 32/36] WIN32: Cleanup header includes for AudioCD code. --- backends/audiocd/win32/win32-audiocd.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp index eb9c472ff91..6c057efdb7a 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 #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 #include "msvc/ntddcdrm.h" #elif defined(__MINGW32__) && !defined(__MINGW64__) // Classic MinGW uses non standard paths for DDK headers. #include #else -#include #include #endif From ad678cf083430e92729ede882fcfa2ff0fa5a2b0 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Tue, 15 Mar 2016 14:13:45 +0100 Subject: [PATCH 33/36] MACOSX: Replace manual uint parsing by strtol. --- backends/audiocd/macosx/macosx-audiocd.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp index ef5b4c453a8..56075f2a608 100644 --- a/backends/audiocd/macosx/macosx-audiocd.cpp +++ b/backends/audiocd/macosx/macosx-audiocd.cpp @@ -44,6 +44,7 @@ #include #include +#include #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()); + } } } } From f75bc481415c0b3e56627aaa84c6151a62c789b5 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Tue, 15 Mar 2016 14:19:16 +0100 Subject: [PATCH 34/36] MACOSX: Guard audio cd code by define check. --- backends/audiocd/macosx/macosx-audiocd.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp index 56075f2a608..d03541a2c9d 100644 --- a/backends/audiocd/macosx/macosx-audiocd.cpp +++ b/backends/audiocd/macosx/macosx-audiocd.cpp @@ -42,6 +42,8 @@ * */ +#ifdef MACOSX + #include #include #include @@ -299,3 +301,5 @@ bool MacOSXAudioCDManager::findTrackNames(const Common::String &drivePath) { AudioCDManager *createMacOSXAudioCDManager() { return new MacOSXAudioCDManager(); } + +#endif // MACOSX From 7725e00e9a10f5bb93ebad2c1befdc448e0d945c Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Tue, 15 Mar 2016 14:23:56 +0100 Subject: [PATCH 35/36] LINUX: Guard Linux audio CD code. --- backends/audiocd/linux/linux-audiocd.cpp | 19 +++++++++++++------ backends/audiocd/linux/linux-audiocd.h | 2 ++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/backends/audiocd/linux/linux-audiocd.cpp b/backends/audiocd/linux/linux-audiocd.cpp index a86860106a1..a5838e1c5ad 100644 --- a/backends/audiocd/linux/linux-audiocd.cpp +++ b/backends/audiocd/linux/linux-audiocd.cpp @@ -42,15 +42,13 @@ * */ -#include -#include -#include -#include -#include -#include +// 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 +#include +#include +#include +#include +#include + 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 69046e936a5..09d6353991a 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; From afe8ea139ccc4e563260494c236642153c540001 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Tue, 15 Mar 2016 14:25:42 +0100 Subject: [PATCH 36/36] MACOSX: Add comment about track ID search loop. --- backends/audiocd/macosx/macosx-audiocd.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/backends/audiocd/macosx/macosx-audiocd.cpp b/backends/audiocd/macosx/macosx-audiocd.cpp index d03541a2c9d..e8d41c3e10c 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++) ;