Initial commit
This commit is contained in:
197
backends/audiocd/audiocd-stream.cpp
Normal file
197
backends/audiocd/audiocd-stream.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Original license header:
|
||||
*
|
||||
* Cabal - Legacy Game Implementations
|
||||
*
|
||||
* Cabal is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backends/audiocd/audiocd-stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
AudioCDStream::AudioCDStream() : _buffer(), _frame(0), _bufferPos(0), _bufferFrame(0), _forceStop(false) {
|
||||
}
|
||||
|
||||
AudioCDStream::~AudioCDStream() {
|
||||
// Stop the timer; the subclass needs to do this,
|
||||
// so this is just a last resort.
|
||||
stopTimer();
|
||||
|
||||
// Clear any buffered frames
|
||||
emptyQueue();
|
||||
}
|
||||
|
||||
int AudioCDStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples = 0;
|
||||
|
||||
// See if any data is left first
|
||||
while (_bufferPos < kSamplesPerFrame && samples < numSamples)
|
||||
buffer[samples++] = _buffer[_bufferPos++];
|
||||
|
||||
// Bail out if done
|
||||
if (endOfData())
|
||||
return samples;
|
||||
|
||||
while (samples < numSamples && !endOfData()) {
|
||||
if (!readNextFrame())
|
||||
return samples;
|
||||
|
||||
// Copy the samples over
|
||||
for (_bufferPos = 0; _bufferPos < kSamplesPerFrame && samples < numSamples;)
|
||||
buffer[samples++] = _buffer[_bufferPos++];
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
bool AudioCDStream::readNextFrame() {
|
||||
// Fetch a frame from the queue
|
||||
int16 *buffer;
|
||||
|
||||
{
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
// Nothing we can do if it's empty
|
||||
if (_bufferQueue.empty())
|
||||
return false;
|
||||
|
||||
buffer = _bufferQueue.pop();
|
||||
}
|
||||
|
||||
memcpy(_buffer, buffer, kSamplesPerFrame * 2);
|
||||
delete[] buffer;
|
||||
_frame++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioCDStream::endOfData() const {
|
||||
return !shouldForceStop() && getStartFrame() + _frame >= getEndFrame() && _bufferPos == kSamplesPerFrame;
|
||||
}
|
||||
|
||||
bool AudioCDStream::seek(const Audio::Timestamp &where) {
|
||||
// Stop the timer
|
||||
stopTimer();
|
||||
|
||||
// Clear anything out of the queue
|
||||
emptyQueue();
|
||||
|
||||
// Convert to the frame number
|
||||
// Really not much else needed
|
||||
_bufferPos = kSamplesPerFrame;
|
||||
_frame = where.convertToFramerate(kFramesPerSecond).totalNumberOfFrames();
|
||||
_bufferFrame = _frame;
|
||||
|
||||
// Start the timer again
|
||||
startTimer();
|
||||
return true;
|
||||
}
|
||||
|
||||
Audio::Timestamp AudioCDStream::getLength() const {
|
||||
return Audio::Timestamp(0, getEndFrame() - getStartFrame(), kFramesPerSecond);
|
||||
}
|
||||
|
||||
void AudioCDStream::timerProc(void *refCon) {
|
||||
static_cast<AudioCDStream *>(refCon)->onTimer();
|
||||
}
|
||||
|
||||
void AudioCDStream::onTimer() {
|
||||
// The goal here is to do as much work in this timer instead
|
||||
// of doing it in the readBuffer() call, which is the mixer.
|
||||
|
||||
// If we're done, bail.
|
||||
if (shouldForceStop() || getStartFrame() + _bufferFrame >= getEndFrame())
|
||||
return;
|
||||
|
||||
// Get a quick count of the number of items in the queue
|
||||
// We don't care that much; we only need a quick estimate
|
||||
_mutex.lock();
|
||||
uint32 queueCount = _bufferQueue.size();
|
||||
_mutex.unlock();
|
||||
|
||||
// If we have enough audio buffered, bail out
|
||||
if (queueCount >= kBufferThreshold)
|
||||
return;
|
||||
|
||||
while (!shouldForceStop() && queueCount < kBufferThreshold && getStartFrame() + _bufferFrame < getEndFrame()) {
|
||||
int16 *buffer = new int16[kSamplesPerFrame];
|
||||
|
||||
// Figure out the MSF of the frame we're looking for
|
||||
int frame = _bufferFrame + getStartFrame();
|
||||
|
||||
// Request to read that frame
|
||||
if (!readFrame(frame, buffer)) {
|
||||
warning("Failed to read CD audio");
|
||||
forceStop();
|
||||
return;
|
||||
}
|
||||
|
||||
_bufferFrame++;
|
||||
|
||||
// Now push the buffer onto the queue
|
||||
Common::StackLock lock(_mutex);
|
||||
_bufferQueue.push(buffer);
|
||||
queueCount = _bufferQueue.size();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioCDStream::startTimer(bool fillBuffer) {
|
||||
_forceStop = false;
|
||||
if (fillBuffer) {
|
||||
onTimer();
|
||||
}
|
||||
g_system->getTimerManager()->installTimerProc(timerProc, 10 * 1000, this, "AudioCDStream");
|
||||
}
|
||||
|
||||
void AudioCDStream::stopTimer() {
|
||||
forceStop();
|
||||
g_system->getTimerManager()->removeTimerProc(timerProc);
|
||||
}
|
||||
|
||||
void AudioCDStream::emptyQueue() {
|
||||
while (!_bufferQueue.empty())
|
||||
delete[] _bufferQueue.pop();
|
||||
}
|
||||
|
||||
bool AudioCDStream::shouldForceStop() const {
|
||||
Common::StackLock lock(_forceStopMutex);
|
||||
return _forceStop;
|
||||
}
|
||||
|
||||
void AudioCDStream::forceStop() {
|
||||
Common::StackLock lock(_forceStopMutex);
|
||||
_forceStop = true;
|
||||
}
|
||||
106
backends/audiocd/audiocd-stream.h
Normal file
106
backends/audiocd/audiocd-stream.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Original license header:
|
||||
*
|
||||
* Cabal - Legacy Game Implementations
|
||||
*
|
||||
* Cabal is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_AUDIOCD_AUDIOCD_STREAM_H
|
||||
#define BACKENDS_AUDIOCD_AUDIOCD_STREAM_H
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/queue.h"
|
||||
#include "common/timer.h"
|
||||
|
||||
class AudioCDStream : public Audio::SeekableAudioStream {
|
||||
public:
|
||||
AudioCDStream();
|
||||
~AudioCDStream();
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
bool isStereo() const { return true; }
|
||||
int getRate() const { return 44100; }
|
||||
bool endOfData() const;
|
||||
bool seek(const Audio::Timestamp &where);
|
||||
Audio::Timestamp getLength() const;
|
||||
|
||||
protected:
|
||||
virtual uint getStartFrame() const = 0;
|
||||
virtual uint getEndFrame() const = 0;
|
||||
virtual bool readFrame(int frame, int16 *buffer) = 0;
|
||||
|
||||
void startTimer(bool fillBuffer = false);
|
||||
void stopTimer();
|
||||
|
||||
enum {
|
||||
kBytesPerFrame = 2352,
|
||||
kSamplesPerFrame = kBytesPerFrame / 2
|
||||
};
|
||||
|
||||
enum {
|
||||
kSecondsPerMinute = 60,
|
||||
kFramesPerSecond = 75
|
||||
};
|
||||
|
||||
enum {
|
||||
// Keep about a second's worth of audio in the buffer
|
||||
kBufferThreshold = kFramesPerSecond
|
||||
};
|
||||
|
||||
private:
|
||||
int16 _buffer[kSamplesPerFrame];
|
||||
int _frame;
|
||||
uint _bufferPos;
|
||||
|
||||
Common::Queue<int16 *> _bufferQueue;
|
||||
int _bufferFrame;
|
||||
Common::Mutex _mutex;
|
||||
|
||||
bool _forceStop;
|
||||
bool shouldForceStop() const;
|
||||
void forceStop();
|
||||
Common::Mutex _forceStopMutex;
|
||||
|
||||
bool readNextFrame();
|
||||
static void timerProc(void *refCon);
|
||||
void onTimer();
|
||||
void emptyQueue();
|
||||
};
|
||||
|
||||
#endif
|
||||
135
backends/audiocd/audiocd.h
Normal file
135
backends/audiocd/audiocd.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_AUDIOCD_ABSTRACT_H
|
||||
#define BACKENDS_AUDIOCD_ABSTRACT_H
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/noncopyable.h"
|
||||
|
||||
/**
|
||||
* Abstract Audio CD manager class. Subclasses implement the actual
|
||||
* functionality.
|
||||
*/
|
||||
class AudioCDManager : Common::NonCopyable {
|
||||
public:
|
||||
virtual ~AudioCDManager() {}
|
||||
|
||||
/**
|
||||
* A structure containing the current playback information
|
||||
*/
|
||||
struct Status {
|
||||
bool playing;
|
||||
int track;
|
||||
int start;
|
||||
int duration;
|
||||
int numLoops;
|
||||
int volume;
|
||||
int balance;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 (<=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
|
||||
* @param soundType What sound type to play as. By default, it's as music
|
||||
* @note The @c onlyEmulate parameter is deprecated.
|
||||
* @return @c true if the track started playing, @c false otherwise
|
||||
*/
|
||||
virtual bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false,
|
||||
Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType) = 0;
|
||||
|
||||
/**
|
||||
* Start audio CD playback at a specific absolute timestamp
|
||||
* @param startFrame the frame at which playback should start (75 frames = 1 second).
|
||||
* @param numLoops how often playback should be repeated (<=0 means infinitely often).
|
||||
* @param duration the number of frames to play.
|
||||
* @param onlyEmulate determines if the track should be emulated only
|
||||
* @param soundType What sound type to play as. By default, it's as music
|
||||
* @param cuesheet The name of the cuesheet to use for timing data
|
||||
* @note The @c onlyEmulate parameter is deprecated.
|
||||
* @return @c true if the track started playing, @c false otherwise
|
||||
*/
|
||||
virtual bool playAbsolute(int startFrame, int numLoops, int duration, bool onlyEmulate = false,
|
||||
Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType, const char *cuesheet = "disc.cue") = 0;
|
||||
|
||||
/**
|
||||
* Get if audio is being played.
|
||||
* @return true if CD audio is playing
|
||||
*/
|
||||
virtual bool isPlaying() const = 0;
|
||||
|
||||
/**
|
||||
* Set the audio volume
|
||||
*/
|
||||
virtual void setVolume(byte volume) = 0;
|
||||
|
||||
/**
|
||||
* Set the speakers balance
|
||||
*/
|
||||
virtual void setBalance(int8 balance) = 0;
|
||||
|
||||
/**
|
||||
* Stop audio playback.
|
||||
*/
|
||||
virtual void stop() = 0;
|
||||
|
||||
/**
|
||||
* Update audio status.
|
||||
*/
|
||||
virtual void update() = 0;
|
||||
|
||||
/**
|
||||
* Get the playback status.
|
||||
* @return a Status struct with playback data.
|
||||
*/
|
||||
virtual Status getStatus() const = 0;
|
||||
|
||||
/**
|
||||
* Checks whether the extracted audio cd tracks exists as files in
|
||||
* the search paths.
|
||||
* @return true if audio files of the expected naming scheme are found, and supported by ScummVM.
|
||||
*/
|
||||
virtual bool existExtractedCDAudioFiles(uint track) = 0;
|
||||
|
||||
/**
|
||||
* Checks if game data are read from the same CD drive which should also play game CD audio.
|
||||
* @return true, if this case is applicable, and the system doesn't allow it.
|
||||
*/
|
||||
virtual bool isDataAndCDAudioReadFromSameCD() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
231
backends/audiocd/default/default-audiocd.cpp
Normal file
231
backends/audiocd/default/default-audiocd.cpp
Normal file
@@ -0,0 +1,231 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backends/audiocd/default/default-audiocd.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/file.h"
|
||||
#include "common/system.h"
|
||||
#include "common/util.h"
|
||||
#include "common/formats/cue.h"
|
||||
|
||||
DefaultAudioCDManager::DefaultAudioCDManager() {
|
||||
_cd.playing = false;
|
||||
_cd.track = 0;
|
||||
_cd.start = 0;
|
||||
_cd.duration = 0;
|
||||
_cd.numLoops = 0;
|
||||
_cd.volume = Audio::Mixer::kMaxChannelVolume;
|
||||
_cd.balance = 0;
|
||||
_mixer = g_system->getMixer();
|
||||
_emulating = false;
|
||||
assert(_mixer);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void DefaultAudioCDManager::fillPotentialTrackNames(Common::Array<Common::String> &trackNames, int track) const {
|
||||
trackNames.reserve(4);
|
||||
trackNames.push_back(Common::String::format("track%d", track));
|
||||
trackNames.push_back(Common::String::format("track%02d", track));
|
||||
trackNames.push_back(Common::String::format("track_%d", track));
|
||||
trackNames.push_back(Common::String::format("track_%02d", track));
|
||||
}
|
||||
|
||||
bool DefaultAudioCDManager::existExtractedCDAudioFiles(uint track) {
|
||||
// keep this in sync with STREAM_FILEFORMATS
|
||||
const char *extensions[] = {
|
||||
#ifdef USE_VORBIS
|
||||
"ogg",
|
||||
#endif
|
||||
#ifdef USE_FLAC
|
||||
"fla", "flac",
|
||||
#endif
|
||||
#ifdef USE_MAD
|
||||
"mp3",
|
||||
#endif
|
||||
"m4a",
|
||||
"wav",
|
||||
nullptr
|
||||
};
|
||||
|
||||
Common::Array<Common::String> trackNames;
|
||||
fillPotentialTrackNames(trackNames, track);
|
||||
|
||||
for (auto &trackName : trackNames) {
|
||||
for (const char **ext = extensions; *ext; ++ext) {
|
||||
const Common::String &filename = Common::String::format("%s.%s", trackName.c_str(), *ext);
|
||||
if (Common::File::exists(Common::Path(filename, '/'))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DefaultAudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate,
|
||||
Audio::Mixer::SoundType soundType) {
|
||||
stop();
|
||||
|
||||
if (numLoops != 0 || startFrame != 0) {
|
||||
_cd.track = track;
|
||||
_cd.numLoops = numLoops;
|
||||
_cd.start = startFrame;
|
||||
_cd.duration = duration;
|
||||
|
||||
// Try to load the track from a compressed data file, and if found, use
|
||||
// that. If not found, attempt to start regular Audio CD playback of
|
||||
// the requested track.
|
||||
Common::Array<Common::String> trackNames;
|
||||
fillPotentialTrackNames(trackNames, track);
|
||||
Audio::SeekableAudioStream *stream = nullptr;
|
||||
|
||||
for (auto &trackName : trackNames) {
|
||||
stream = Audio::SeekableAudioStream::openStreamFile(Common::Path(trackName, '/'));
|
||||
if (stream)
|
||||
break;
|
||||
}
|
||||
|
||||
if (stream != nullptr) {
|
||||
Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75);
|
||||
Audio::Timestamp end = duration ? Audio::Timestamp(0, startFrame + duration, 75) : stream->getLength();
|
||||
|
||||
/*
|
||||
FIXME: Seems numLoops == 0 and numLoops == 1 both indicate a single repetition,
|
||||
while all other positive numbers indicate precisely the number of desired
|
||||
repetitions. Finally, -1 means infinitely many
|
||||
*/
|
||||
_emulating = true;
|
||||
_mixer->playStream(soundType, &_handle,
|
||||
Audio::makeLoopingAudioStream(stream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops), -1, _cd.volume, _cd.balance);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DefaultAudioCDManager::playAbsolute(int startFrame, int numLoops, int duration, bool onlyEmulate,
|
||||
Audio::Mixer::SoundType soundType, const char *cuesheet) {
|
||||
|
||||
Common::File cuefile;
|
||||
if (!cuefile.open(cuesheet)) {
|
||||
return false;
|
||||
}
|
||||
Common::String cuestring = cuefile.readString(0, cuefile.size());
|
||||
Common::CueSheet cue(cuestring.c_str());
|
||||
|
||||
Common::CueSheet::CueTrack *track = cue.getTrackAtFrame(startFrame);
|
||||
if (track == nullptr) {
|
||||
warning("Unable to locate track for frame %i", startFrame);
|
||||
return false;
|
||||
} else {
|
||||
warning("Playing from frame %i", startFrame);
|
||||
}
|
||||
int firstFrame = track->indices[0] == -1 ? track->indices[1] : track->indices[0];
|
||||
|
||||
return play(track->number, numLoops, startFrame - firstFrame, duration, onlyEmulate);
|
||||
}
|
||||
|
||||
void DefaultAudioCDManager::stop() {
|
||||
if (_emulating) {
|
||||
// Audio CD emulation
|
||||
_mixer->stopHandle(_handle);
|
||||
_emulating = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DefaultAudioCDManager::isPlaying() const {
|
||||
// Audio CD emulation
|
||||
if (_emulating)
|
||||
return _mixer->isSoundHandleActive(_handle);
|
||||
|
||||
// The default class only handles emulation
|
||||
return false;
|
||||
}
|
||||
|
||||
void DefaultAudioCDManager::setVolume(byte volume) {
|
||||
_cd.volume = volume;
|
||||
|
||||
// Audio CD emulation
|
||||
if (_emulating && isPlaying())
|
||||
_mixer->setChannelVolume(_handle, _cd.volume);
|
||||
}
|
||||
|
||||
void DefaultAudioCDManager::setBalance(int8 balance) {
|
||||
_cd.balance = balance;
|
||||
|
||||
// Audio CD emulation
|
||||
if (_emulating && isPlaying())
|
||||
_mixer->setChannelBalance(_handle, _cd.balance);
|
||||
}
|
||||
|
||||
void DefaultAudioCDManager::update() {
|
||||
if (_emulating) {
|
||||
// Check whether the audio track stopped playback
|
||||
if (!_mixer->isSoundHandleActive(_handle)) {
|
||||
// FIXME: We do not update the numLoops parameter here (and in fact,
|
||||
// currently can't do that). Luckily, only one engine ever checks
|
||||
// this part of the AudioCD status, namely the SCUMM engine; and it
|
||||
// only checks whether the track is currently set to infinite looping
|
||||
// or not.
|
||||
_emulating = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DefaultAudioCDManager::Status DefaultAudioCDManager::getStatus() const {
|
||||
Status info = _cd;
|
||||
info.playing = isPlaying();
|
||||
return info;
|
||||
}
|
||||
|
||||
bool DefaultAudioCDManager::openRealCD() {
|
||||
Common::String cdrom = ConfMan.get("cdrom");
|
||||
|
||||
// Try to parse it as an int
|
||||
char *endPos;
|
||||
int drive = strtol(cdrom.c_str(), &endPos, 0);
|
||||
|
||||
// If not an integer, treat as a drive path
|
||||
if (endPos == cdrom.c_str())
|
||||
return openCD(Common::Path::fromConfig(cdrom));
|
||||
|
||||
if (drive < 0)
|
||||
return false;
|
||||
|
||||
return openCD(drive);
|
||||
}
|
||||
|
||||
85
backends/audiocd/default/default-audiocd.h
Normal file
85
backends/audiocd/default/default-audiocd.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_AUDIOCD_DEFAULT_H
|
||||
#define BACKENDS_AUDIOCD_DEFAULT_H
|
||||
|
||||
#include "backends/audiocd/audiocd.h"
|
||||
#include "audio/mixer.h"
|
||||
|
||||
namespace Common {
|
||||
class String;
|
||||
} // End of namespace Common
|
||||
|
||||
/**
|
||||
* The default audio cd manager. Implements emulation of audio cd playback.
|
||||
*/
|
||||
class DefaultAudioCDManager : public AudioCDManager {
|
||||
public:
|
||||
DefaultAudioCDManager();
|
||||
virtual ~DefaultAudioCDManager();
|
||||
|
||||
virtual bool open();
|
||||
virtual void close();
|
||||
virtual bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate = false,
|
||||
Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType);
|
||||
virtual bool playAbsolute(int startFrame, int numLoops, int duration, bool onlyEmulate = false,
|
||||
Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType, const char *cuesheet = "disc.cue");
|
||||
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
|
||||
virtual bool existExtractedCDAudioFiles(uint track);
|
||||
virtual bool isDataAndCDAudioReadFromSameCD() { return false; }
|
||||
|
||||
private:
|
||||
void fillPotentialTrackNames(Common::Array<Common::String> &trackNames, int track) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Open a CD using the cdrom config variable
|
||||
*/
|
||||
bool openRealCD();
|
||||
|
||||
/**
|
||||
* Open a CD using the specified drive index
|
||||
* @param drive The index of the drive
|
||||
* @note The index is implementation-defined, but 0 is always the best choice
|
||||
*/
|
||||
virtual bool openCD(int drive) { return false; }
|
||||
|
||||
/**
|
||||
* 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::Path &drive) { return false; }
|
||||
|
||||
Audio::SoundHandle _handle;
|
||||
bool _emulating;
|
||||
|
||||
Status _cd;
|
||||
Audio::Mixer *_mixer;
|
||||
};
|
||||
|
||||
#endif
|
||||
473
backends/audiocd/linux/linux-audiocd.cpp
Normal file
473
backends/audiocd/linux/linux-audiocd.cpp
Normal file
@@ -0,0 +1,473 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Original license header:
|
||||
*
|
||||
* Cabal - Legacy Game Implementations
|
||||
*
|
||||
* Cabal is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Enable all forbidden symbols to allow us to include and use necessary APIs.
|
||||
#define FORBIDDEN_SYMBOL_ALLOW_ALL
|
||||
|
||||
#include "backends/audiocd/linux/linux-audiocd.h"
|
||||
|
||||
#ifdef USE_LINUXCD
|
||||
|
||||
#include "backends/audiocd/audiocd-stream.h"
|
||||
#include "backends/audiocd/default/default-audiocd.h"
|
||||
#include "common/array.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/str.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/cdrom.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
enum {
|
||||
kLeadoutTrack = 0xAA
|
||||
};
|
||||
|
||||
enum {
|
||||
kBytesPerFrame = 2352,
|
||||
kSamplesPerFrame = kBytesPerFrame / 2
|
||||
};
|
||||
|
||||
enum {
|
||||
kSecondsPerMinute = 60,
|
||||
kFramesPerSecond = 75
|
||||
};
|
||||
|
||||
enum {
|
||||
// Keep about a second's worth of audio in the buffer
|
||||
kBufferThreshold = kFramesPerSecond
|
||||
};
|
||||
|
||||
static int getFrameCount(const cdrom_msf0 &msf) {
|
||||
int time = msf.minute;
|
||||
time *= kSecondsPerMinute;
|
||||
time += msf.second;
|
||||
time *= kFramesPerSecond;
|
||||
time += msf.frame;
|
||||
return time;
|
||||
}
|
||||
|
||||
// Helper function to convert an error code into a human-readable message
|
||||
static Common::String getErrorMessage(int errorCode) {
|
||||
char buf[256];
|
||||
buf[0] = 0;
|
||||
|
||||
#ifdef _GNU_SOURCE
|
||||
// glibc sucks
|
||||
return Common::String(strerror_r(errorCode, buf, sizeof(buf)));
|
||||
#else
|
||||
strerror_r(errorCode, buf, sizeof(buf));
|
||||
return Common::String(buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
class LinuxAudioCDStream : public AudioCDStream {
|
||||
public:
|
||||
LinuxAudioCDStream(int fd, const cdrom_tocentry &startEntry, const cdrom_tocentry &endEntry);
|
||||
~LinuxAudioCDStream();
|
||||
|
||||
protected:
|
||||
uint getStartFrame() const;
|
||||
uint getEndFrame() const;
|
||||
bool readFrame(int frame, int16 *buffer);
|
||||
|
||||
private:
|
||||
int _fd;
|
||||
const cdrom_tocentry &_startEntry, &_endEntry;
|
||||
};
|
||||
|
||||
LinuxAudioCDStream::LinuxAudioCDStream(int fd, const cdrom_tocentry &startEntry, const cdrom_tocentry &endEntry) :
|
||||
_fd(fd), _startEntry(startEntry), _endEntry(endEntry) {
|
||||
// We fill the buffer here already to prevent any out of sync issues due
|
||||
// to the CD not yet having spun up.
|
||||
startTimer(true);
|
||||
}
|
||||
|
||||
LinuxAudioCDStream::~LinuxAudioCDStream() {
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
bool LinuxAudioCDStream::readFrame(int frame, int16 *buffer) {
|
||||
// Create the argument
|
||||
union {
|
||||
cdrom_msf msf;
|
||||
char buffer[kBytesPerFrame];
|
||||
} arg;
|
||||
|
||||
int seconds = frame / kFramesPerSecond;
|
||||
frame %= kFramesPerSecond;
|
||||
int minutes = seconds / kSecondsPerMinute;
|
||||
seconds %= kSecondsPerMinute;
|
||||
|
||||
// Request to read that frame
|
||||
// We don't use CDROMREADAUDIO, as it seems to cause kernel
|
||||
// panics on ejecting discs. Probably bad to eject the disc
|
||||
// while playing, but at least let's try to prevent that case.
|
||||
arg.msf.cdmsf_min0 = minutes;
|
||||
arg.msf.cdmsf_sec0 = seconds;
|
||||
arg.msf.cdmsf_frame0 = frame;
|
||||
// The "end" part is irrelevant (why isn't cdrom_msf0 the type
|
||||
// instead?)
|
||||
|
||||
if (ioctl(_fd, CDROMREADRAW, &arg) < 0) {
|
||||
warning("Failed to CD read audio: %s", getErrorMessage(errno).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(buffer, arg.buffer, kBytesPerFrame);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint LinuxAudioCDStream::getStartFrame() const {
|
||||
return getFrameCount(_startEntry.cdte_addr.msf);
|
||||
}
|
||||
|
||||
uint LinuxAudioCDStream::getEndFrame() const {
|
||||
return getFrameCount(_endEntry.cdte_addr.msf);
|
||||
}
|
||||
|
||||
|
||||
class LinuxAudioCDManager : public DefaultAudioCDManager {
|
||||
public:
|
||||
LinuxAudioCDManager();
|
||||
~LinuxAudioCDManager();
|
||||
|
||||
bool open() override;
|
||||
void close() override;
|
||||
bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate,
|
||||
Audio::Mixer::SoundType soundType) override;
|
||||
|
||||
protected:
|
||||
bool openCD(int drive) override;
|
||||
bool openCD(const Common::Path &drive) override;
|
||||
|
||||
private:
|
||||
struct Device {
|
||||
Device(const Common::Path &n, dev_t d) : name(n), device(d) {}
|
||||
Common::Path name;
|
||||
dev_t device;
|
||||
};
|
||||
|
||||
typedef Common::Array<Device> DeviceList;
|
||||
DeviceList scanDevices();
|
||||
bool tryAddDrive(DeviceList &devices, const Common::Path &drive);
|
||||
bool tryAddDrive(DeviceList &devices, const Common::Path &drive, dev_t device);
|
||||
bool tryAddDrive(DeviceList &devices, dev_t device);
|
||||
bool tryAddPath(DeviceList &devices, const Common::Path &path);
|
||||
bool tryAddGamePath(DeviceList &devices);
|
||||
bool loadTOC();
|
||||
static bool hasDevice(const DeviceList &devices, dev_t device);
|
||||
|
||||
int _fd;
|
||||
cdrom_tochdr _tocHeader;
|
||||
Common::Array<cdrom_tocentry> _tocEntries;
|
||||
};
|
||||
|
||||
static bool isTrayEmpty(int errorNumber) {
|
||||
switch (errorNumber) {
|
||||
case EIO:
|
||||
case ENOENT:
|
||||
case EINVAL:
|
||||
#ifdef ENOMEDIUM
|
||||
case ENOMEDIUM:
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LinuxAudioCDManager::LinuxAudioCDManager() {
|
||||
_fd = -1;
|
||||
memset(&_tocHeader, 0, sizeof(_tocHeader));
|
||||
}
|
||||
|
||||
LinuxAudioCDManager::~LinuxAudioCDManager() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool LinuxAudioCDManager::open() {
|
||||
close();
|
||||
|
||||
if (openRealCD())
|
||||
return true;
|
||||
|
||||
return DefaultAudioCDManager::open();
|
||||
}
|
||||
|
||||
void LinuxAudioCDManager::close() {
|
||||
DefaultAudioCDManager::close();
|
||||
|
||||
if (_fd < 0)
|
||||
return;
|
||||
|
||||
::close(_fd);
|
||||
_fd = -1;
|
||||
memset(&_tocHeader, 0, sizeof(_tocHeader));
|
||||
_tocEntries.clear();
|
||||
}
|
||||
|
||||
bool LinuxAudioCDManager::openCD(int drive) {
|
||||
DeviceList devices = scanDevices();
|
||||
if (drive >= (int)devices.size())
|
||||
return false;
|
||||
|
||||
_fd = ::open(devices[drive].name.toString('/').c_str(), O_RDONLY | O_NONBLOCK, 0);
|
||||
if (_fd < 0)
|
||||
return false;
|
||||
|
||||
if (!loadTOC()) {
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxAudioCDManager::openCD(const Common::Path &drive) {
|
||||
DeviceList devices;
|
||||
if (!tryAddDrive(devices, drive) && !tryAddPath(devices, drive))
|
||||
return false;
|
||||
|
||||
_fd = ::open(devices[0].name.toString('/').c_str(), O_RDONLY | O_NONBLOCK, 0);
|
||||
if (_fd < 0)
|
||||
return false;
|
||||
|
||||
if (!loadTOC()) {
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxAudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate,
|
||||
Audio::Mixer::SoundType soundType) {
|
||||
// Prefer emulation
|
||||
if (DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate, soundType))
|
||||
return true;
|
||||
|
||||
// If we're set to only emulate, or have no CD drive, return here
|
||||
if (onlyEmulate || _fd < 0)
|
||||
return false;
|
||||
|
||||
// HACK: For now, just assume that track number is right
|
||||
// That only works because ScummVM uses the wrong track number anyway
|
||||
|
||||
if (track >= (int)_tocEntries.size() - 1) {
|
||||
warning("No such track %d", track);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bail if the track isn't an audio track
|
||||
if ((_tocEntries[track].cdte_ctrl & 0x04) != 0) {
|
||||
warning("Track %d is not audio", track);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the AudioStream and play it
|
||||
debug(1, "Playing CD track %d", track);
|
||||
|
||||
Audio::SeekableAudioStream *audioStream = new LinuxAudioCDStream(_fd, _tocEntries[track], _tocEntries[track + 1]);
|
||||
|
||||
Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75);
|
||||
Audio::Timestamp end = (duration == 0) ? audioStream->getLength() : Audio::Timestamp(0, startFrame + duration, 75);
|
||||
|
||||
// Fake emulation since we're really playing an AudioStream
|
||||
_emulating = true;
|
||||
|
||||
_mixer->playStream(
|
||||
soundType,
|
||||
&_handle,
|
||||
Audio::makeLoopingAudioStream(audioStream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops),
|
||||
-1,
|
||||
_cd.volume,
|
||||
_cd.balance,
|
||||
DisposeAfterUse::YES,
|
||||
true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LinuxAudioCDManager::DeviceList LinuxAudioCDManager::scanDevices() {
|
||||
DeviceList devices;
|
||||
|
||||
// Try to use the game's path first as the device
|
||||
tryAddGamePath(devices);
|
||||
|
||||
// Try adding the default CD-ROM
|
||||
tryAddDrive(devices, "/dev/cdrom");
|
||||
|
||||
// TODO: Try others?
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
bool LinuxAudioCDManager::tryAddDrive(DeviceList &devices, const Common::Path &drive) {
|
||||
struct stat stbuf;
|
||||
if (stat(drive.toString('/').c_str(), &stbuf) < 0)
|
||||
return false;
|
||||
|
||||
// Must be a character or block device
|
||||
if (!S_ISCHR(stbuf.st_mode) && !S_ISBLK(stbuf.st_mode))
|
||||
return false;
|
||||
|
||||
return tryAddDrive(devices, drive, stbuf.st_rdev);
|
||||
}
|
||||
|
||||
bool LinuxAudioCDManager::tryAddDrive(DeviceList &devices, const Common::Path &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.toString('/').c_str(), O_RDONLY | O_NONBLOCK, 0);
|
||||
if (fd >= 0) {
|
||||
cdrom_subchnl info;
|
||||
info.cdsc_format = CDROM_MSF;
|
||||
|
||||
bool isCD = ioctl(fd, CDROMSUBCHNL, &info) == 0 || isTrayEmpty(errno);
|
||||
::close(fd);
|
||||
if (isCD) {
|
||||
devices.push_back(Device(drive, device));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LinuxAudioCDManager::tryAddDrive(DeviceList &devices, dev_t device) {
|
||||
// Construct the block name
|
||||
// TODO: libblkid's blkid_devno_to_devname is exactly what we look for.
|
||||
// This requires an external dependency though.
|
||||
Common::Path name(Common::String::format("/dev/block/%d:%d", major(device), minor(device)), '/');
|
||||
|
||||
return tryAddDrive(devices, name, device);
|
||||
}
|
||||
|
||||
bool LinuxAudioCDManager::tryAddPath(DeviceList &devices, const Common::Path &path) {
|
||||
struct stat stbuf;
|
||||
if (stat(path.toString(Common::Path::kNativeSeparator).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.getPath("path"));
|
||||
}
|
||||
|
||||
bool LinuxAudioCDManager::loadTOC() {
|
||||
if (_fd < 0)
|
||||
return false;
|
||||
|
||||
if (ioctl(_fd, CDROMREADTOCHDR, &_tocHeader) < 0)
|
||||
return false;
|
||||
|
||||
debug(4, "CD: Start Track: %d, End Track %d", _tocHeader.cdth_trk0, _tocHeader.cdth_trk1);
|
||||
|
||||
for (int i = _tocHeader.cdth_trk0; i <= _tocHeader.cdth_trk1; i++) {
|
||||
cdrom_tocentry entry;
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
entry.cdte_track = i;
|
||||
entry.cdte_format = CDROM_MSF;
|
||||
|
||||
if (ioctl(_fd, CDROMREADTOCENTRY, &entry) < 0)
|
||||
return false;
|
||||
|
||||
#if 0
|
||||
debug("Entry:");
|
||||
debug("\tTrack: %d", entry.cdte_track);
|
||||
debug("\tAdr: %d", entry.cdte_adr);
|
||||
debug("\tCtrl: %d", entry.cdte_ctrl);
|
||||
debug("\tFormat: %d", entry.cdte_format);
|
||||
debug("\tMSF: %d:%d:%d", entry.cdte_addr.msf.minute, entry.cdte_addr.msf.second, entry.cdte_addr.msf.frame);
|
||||
debug("\tMode: %d\n", entry.cdte_datamode);
|
||||
#endif
|
||||
|
||||
_tocEntries.push_back(entry);
|
||||
}
|
||||
|
||||
// Fetch the leadout so we can get the length of the last frame
|
||||
cdrom_tocentry entry;
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
entry.cdte_track = kLeadoutTrack;
|
||||
entry.cdte_format = CDROM_MSF;
|
||||
|
||||
if (ioctl(_fd, CDROMREADTOCENTRY, &entry) < 0)
|
||||
return false;
|
||||
|
||||
#if 0
|
||||
debug("Lead out:");
|
||||
debug("\tTrack: %d", entry.cdte_track);
|
||||
debug("\tAdr: %d", entry.cdte_adr);
|
||||
debug("\tCtrl: %d", entry.cdte_ctrl);
|
||||
debug("\tFormat: %d", entry.cdte_format);
|
||||
debug("\tMSF: %d:%d:%d", entry.cdte_addr.msf.minute, entry.cdte_addr.msf.second, entry.cdte_addr.msf.frame);
|
||||
debug("\tMode: %d\n", entry.cdte_datamode);
|
||||
#endif
|
||||
|
||||
_tocEntries.push_back(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxAudioCDManager::hasDevice(const DeviceList &devices, dev_t device) {
|
||||
for (DeviceList::const_iterator it = devices.begin(); it != devices.end(); it++)
|
||||
if (it->device == device)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AudioCDManager *createLinuxAudioCDManager() {
|
||||
return new LinuxAudioCDManager();
|
||||
}
|
||||
|
||||
#endif // USE_LINUXCD
|
||||
60
backends/audiocd/linux/linux-audiocd.h
Normal file
60
backends/audiocd/linux/linux-audiocd.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Original license header:
|
||||
*
|
||||
* Cabal - Legacy Game Implementations
|
||||
*
|
||||
* Cabal is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_AUDIOCD_LINUX_H
|
||||
#define BACKENDS_AUDIOCD_LINUX_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#ifdef USE_LINUXCD
|
||||
|
||||
class AudioCDManager;
|
||||
|
||||
/**
|
||||
* Create an audio CD manager using the Linux CDROM API
|
||||
*/
|
||||
AudioCDManager *createLinuxAudioCDManager();
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
310
backends/audiocd/macosx/macosx-audiocd.cpp
Normal file
310
backends/audiocd/macosx/macosx-audiocd.cpp
Normal file
@@ -0,0 +1,310 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Original license header:
|
||||
*
|
||||
* Cabal - Legacy Game Implementations
|
||||
*
|
||||
* Cabal is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef MACOSX
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/ucred.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/aiff.h"
|
||||
#include "audio/timestamp.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/fs.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "backends/audiocd/default/default-audiocd.h"
|
||||
#include "backends/audiocd/macosx/macosx-audiocd.h"
|
||||
#include "backends/fs/stdiostream.h"
|
||||
|
||||
// Partially based on SDL's code
|
||||
|
||||
/**
|
||||
* The macOS audio cd manager. Implements real audio cd playback.
|
||||
*/
|
||||
class MacOSXAudioCDManager : public DefaultAudioCDManager {
|
||||
public:
|
||||
MacOSXAudioCDManager() {}
|
||||
~MacOSXAudioCDManager();
|
||||
|
||||
bool open() override;
|
||||
void close() override;
|
||||
bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate,
|
||||
Audio::Mixer::SoundType soundType) override;
|
||||
|
||||
protected:
|
||||
bool openCD(int drive) override;
|
||||
bool openCD(const Common::Path &drive) override;
|
||||
|
||||
private:
|
||||
struct Drive {
|
||||
Drive(const Common::Path &m, const Common::Path &d, const Common::String &f) :
|
||||
mountPoint(m), deviceName(d), fsType(f) {}
|
||||
|
||||
Common::Path mountPoint;
|
||||
Common::Path deviceName;
|
||||
Common::String fsType;
|
||||
};
|
||||
|
||||
typedef Common::Array<Drive> DriveList;
|
||||
DriveList detectAllDrives();
|
||||
|
||||
bool findTrackNames(const Common::Path &drivePath);
|
||||
|
||||
Common::HashMap<uint, Common::Path> _trackMap;
|
||||
};
|
||||
|
||||
MacOSXAudioCDManager::~MacOSXAudioCDManager() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool MacOSXAudioCDManager::open() {
|
||||
close();
|
||||
|
||||
if (openRealCD())
|
||||
return true;
|
||||
|
||||
return DefaultAudioCDManager::open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the base disk number of device name.
|
||||
* Returns -1 if mount point is not /dev/disk*
|
||||
*/
|
||||
static int findBaseDiskNumber(const Common::Path &diskPath) {
|
||||
Common::String diskName(diskPath.toString('/'));
|
||||
if (!diskName.hasPrefix("/dev/disk"))
|
||||
return -1;
|
||||
|
||||
const char *startPtr = diskName.c_str() + 9;
|
||||
char *endPtr;
|
||||
int baseDiskNumber = strtol(startPtr, &endPtr, 10);
|
||||
if (startPtr == endPtr)
|
||||
return -1;
|
||||
|
||||
return baseDiskNumber;
|
||||
}
|
||||
|
||||
bool MacOSXAudioCDManager::openCD(int drive) {
|
||||
DriveList allDrives = detectAllDrives();
|
||||
if (allDrives.empty())
|
||||
return false;
|
||||
|
||||
DriveList cddaDrives;
|
||||
|
||||
// Try to get the volume related to the game's path
|
||||
if (ConfMan.hasKey("path")) {
|
||||
Common::String gamePath = ConfMan.getPath("path").toString(Common::Path::kNativeSeparator);
|
||||
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.toString(Common::Path::kNativeSeparator).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.toString(Common::Path::kNativeSeparator).c_str());
|
||||
|
||||
return findTrackNames(cddaDrives[drive].mountPoint);
|
||||
}
|
||||
|
||||
bool MacOSXAudioCDManager::openCD(const Common::Path &drive) {
|
||||
DriveList drives = detectAllDrives();
|
||||
|
||||
for (uint32 i = 0; i < drives.size(); i++) {
|
||||
if (drives[i].fsType != "cddafs")
|
||||
continue;
|
||||
|
||||
if (drives[i].mountPoint == drive || drives[i].deviceName == drive) {
|
||||
debug(1, "Using '%s' as the CD drive", drives[i].mountPoint.toString(Common::Path::kNativeSeparator).c_str());
|
||||
return findTrackNames(drives[i].mountPoint);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MacOSXAudioCDManager::close() {
|
||||
DefaultAudioCDManager::close();
|
||||
_trackMap.clear();
|
||||
}
|
||||
|
||||
MacOSXAudioCDManager::DriveList MacOSXAudioCDManager::detectAllDrives() {
|
||||
int foundDrives = getfsstat(nullptr, 0, MNT_WAIT);
|
||||
if (foundDrives <= 0)
|
||||
return DriveList();
|
||||
|
||||
// Fetch the lists of drives
|
||||
struct statfs *driveStats = (struct statfs *)malloc(sizeof(struct statfs) * foundDrives);
|
||||
foundDrives = getfsstat(driveStats, sizeof(struct statfs) * foundDrives, MNT_NOWAIT);
|
||||
if (foundDrives <= 0) {
|
||||
free(driveStats);
|
||||
return DriveList();
|
||||
}
|
||||
|
||||
DriveList drives;
|
||||
for (int i = 0; i < foundDrives; i++)
|
||||
drives.push_back(Drive(Common::Path(driveStats[i].f_mntonname, Common::Path::kNativeSeparator),
|
||||
Common::Path(driveStats[i].f_mntfromname, Common::Path::kNativeSeparator), driveStats[i].f_fstypename));
|
||||
|
||||
free(driveStats);
|
||||
return drives;
|
||||
}
|
||||
|
||||
bool MacOSXAudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate,
|
||||
Audio::Mixer::SoundType soundType) {
|
||||
// Prefer emulation
|
||||
if (DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate, soundType))
|
||||
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::Path fileName = _trackMap[track];
|
||||
Common::SeekableReadStream *stream = StdioStream::makeFromPath(fileName.toString(Common::Path::kNativeSeparator).c_str(), StdioStream::WriteMode_Read);
|
||||
|
||||
if (!stream) {
|
||||
warning("Failed to open track '%s'", fileName.toString(Common::Path::kNativeSeparator).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
Audio::AudioStream *audioStream = Audio::makeAIFFStream(stream, DisposeAfterUse::YES);
|
||||
if (!audioStream) {
|
||||
warning("Track '%s' is not an AIFF track", fileName.toString(Common::Path::kNativeSeparator).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
Audio::SeekableAudioStream *seekStream = dynamic_cast<Audio::SeekableAudioStream *>(audioStream);
|
||||
if (!seekStream) {
|
||||
warning("Track '%s' is not seekable", fileName.toString(Common::Path::kNativeSeparator).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75);
|
||||
Audio::Timestamp end = duration ? Audio::Timestamp(0, startFrame + duration, 75) : seekStream->getLength();
|
||||
|
||||
// Fake emulation since we're really playing an AIFF file
|
||||
_emulating = true;
|
||||
|
||||
_mixer->playStream(soundType, &_handle,
|
||||
Audio::makeLoopingAudioStream(seekStream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops), -1, _cd.volume, _cd.balance);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacOSXAudioCDManager::findTrackNames(const Common::Path &drivePath) {
|
||||
Common::FSNode directory(drivePath);
|
||||
|
||||
if (!directory.exists()) {
|
||||
warning("Directory '%s' does not exist", drivePath.toString(Common::Path::kNativeSeparator).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!directory.isDirectory()) {
|
||||
warning("'%s' is not a directory", drivePath.toString(Common::Path::kNativeSeparator).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::FSList children;
|
||||
if (!directory.getChildren(children, Common::FSNode::kListFilesOnly) || children.empty()) {
|
||||
warning("Failed to find children for '%s'", drivePath.toString(Common::Path::kNativeSeparator).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < children.size(); i++) {
|
||||
if (!children[i].isDirectory()) {
|
||||
Common::String fileName = children[i].getFileName();
|
||||
|
||||
if (fileName.hasSuffix(".aiff") || fileName.hasSuffix(".cdda")) {
|
||||
uint j = 0;
|
||||
|
||||
// Search for the track ID in the file name.
|
||||
for (; j < fileName.size() && !Common::isDigit(fileName[j]); j++)
|
||||
;
|
||||
|
||||
const char *trackIDString = fileName.c_str() + j;
|
||||
char *endPtr = nullptr;
|
||||
long trackID = strtol(trackIDString, &endPtr, 10);
|
||||
|
||||
if (trackIDString != endPtr && trackID > 0 && (unsigned long)trackID < UINT_MAX) {
|
||||
_trackMap[trackID - 1] = drivePath.appendComponent(fileName);
|
||||
} else {
|
||||
warning("Invalid track file name: '%s'", fileName.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AudioCDManager *createMacOSXAudioCDManager() {
|
||||
return new MacOSXAudioCDManager();
|
||||
}
|
||||
|
||||
#endif // MACOSX
|
||||
59
backends/audiocd/macosx/macosx-audiocd.h
Normal file
59
backends/audiocd/macosx/macosx-audiocd.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Original license header:
|
||||
*
|
||||
* Cabal - Legacy Game Implementations
|
||||
*
|
||||
* Cabal is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_AUDIOCD_MACOSX_H
|
||||
#define BACKENDS_AUDIOCD_MACOSX_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#ifdef MACOSX
|
||||
|
||||
class AudioCDManager;
|
||||
|
||||
/**
|
||||
* Create an audio CD manager for macOS
|
||||
*/
|
||||
AudioCDManager *createMacOSXAudioCDManager();
|
||||
|
||||
#endif
|
||||
|
||||
#endif //
|
||||
172
backends/audiocd/sdl/sdl-audiocd.cpp
Normal file
172
backends/audiocd/sdl/sdl-audiocd.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#if defined(SDL_BACKEND)
|
||||
|
||||
#include "backends/audiocd/sdl/sdl-audiocd.h"
|
||||
|
||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
SdlAudioCDManager::SdlAudioCDManager()
|
||||
:
|
||||
_cdrom(0),
|
||||
_cdTrack(0),
|
||||
_cdNumLoops(0),
|
||||
_cdStartFrame(0),
|
||||
_cdDuration(0),
|
||||
_cdEndTime(0),
|
||||
_cdStopTime(0) {
|
||||
|
||||
}
|
||||
|
||||
SdlAudioCDManager::~SdlAudioCDManager() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool SdlAudioCDManager::open() {
|
||||
close();
|
||||
|
||||
if (openRealCD())
|
||||
return true;
|
||||
|
||||
return DefaultAudioCDManager::open();
|
||||
}
|
||||
|
||||
bool SdlAudioCDManager::openCD(int drive) {
|
||||
if (SDL_InitSubSystem(SDL_INIT_CDROM) == -1)
|
||||
_cdrom = NULL;
|
||||
else {
|
||||
_cdrom = SDL_CDOpen(drive);
|
||||
// Did it open? Check if _cdrom is NULL
|
||||
if (!_cdrom) {
|
||||
warning("Couldn't open drive: %s", SDL_GetError());
|
||||
} else {
|
||||
_cdNumLoops = 0;
|
||||
_cdStopTime = 0;
|
||||
_cdEndTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return (_cdrom != NULL);
|
||||
}
|
||||
|
||||
void SdlAudioCDManager::close() {
|
||||
DefaultAudioCDManager::close();
|
||||
|
||||
if (_cdrom) {
|
||||
SDL_CDStop(_cdrom);
|
||||
SDL_CDClose(_cdrom);
|
||||
_cdrom = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SdlAudioCDManager::stop() {
|
||||
DefaultAudioCDManager::stop();
|
||||
|
||||
// Stop CD Audio in 1/10th of a second
|
||||
_cdStopTime = SDL_GetTicks() + 100;
|
||||
_cdNumLoops = 0;
|
||||
}
|
||||
|
||||
bool SdlAudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate,
|
||||
Audio::Mixer::SoundType soundType) {
|
||||
// Prefer emulation
|
||||
if (DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate, soundType))
|
||||
return true;
|
||||
|
||||
// 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 = numLoops;
|
||||
_cdStartFrame = startFrame;
|
||||
|
||||
SDL_CDStatus(_cdrom);
|
||||
if (startFrame == 0 && duration == 0)
|
||||
SDL_CDPlayTracks(_cdrom, track, 0, 1, 0);
|
||||
else
|
||||
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::isPlaying() const {
|
||||
if (DefaultAudioCDManager::isPlaying())
|
||||
return true;
|
||||
|
||||
if (!_cdrom)
|
||||
return false;
|
||||
|
||||
return (_cdNumLoops != 0 && (SDL_GetTicks() < _cdEndTime || SDL_CDStatus(_cdrom) == CD_PLAYING));
|
||||
}
|
||||
|
||||
void SdlAudioCDManager::update() {
|
||||
DefaultAudioCDManager::update();
|
||||
|
||||
if (!_cdrom)
|
||||
return;
|
||||
|
||||
if (_cdStopTime != 0 && SDL_GetTicks() >= _cdStopTime) {
|
||||
SDL_CDStop(_cdrom);
|
||||
_cdNumLoops = 0;
|
||||
_cdStopTime = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_cdNumLoops == 0 || SDL_GetTicks() < _cdEndTime)
|
||||
return;
|
||||
|
||||
if (_cdNumLoops != 1 && SDL_CDStatus(_cdrom) != CD_STOPPED) {
|
||||
// Wait another second for it to be done
|
||||
_cdEndTime += 1000;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_cdNumLoops > 0)
|
||||
_cdNumLoops--;
|
||||
|
||||
if (_cdNumLoops != 0) {
|
||||
if (_cdStartFrame == 0 && _cdDuration == 0)
|
||||
SDL_CDPlayTracks(_cdrom, _cdTrack, 0, 1, 0);
|
||||
else
|
||||
SDL_CDPlayTracks(_cdrom, _cdTrack, _cdStartFrame, 0, _cdDuration);
|
||||
_cdEndTime = SDL_GetTicks() + _cdrom->track[_cdTrack].length * 1000 / CD_FPS;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
|
||||
#endif
|
||||
57
backends/audiocd/sdl/sdl-audiocd.h
Normal file
57
backends/audiocd/sdl/sdl-audiocd.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_AUDIOCD_SDL_H
|
||||
#define BACKENDS_AUDIOCD_SDL_H
|
||||
|
||||
#include "backends/audiocd/default/default-audiocd.h"
|
||||
|
||||
#include "backends/platform/sdl/sdl-sys.h"
|
||||
|
||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
|
||||
/**
|
||||
* The SDL audio cd manager. Implements real audio cd playback.
|
||||
*/
|
||||
class SdlAudioCDManager : public DefaultAudioCDManager {
|
||||
public:
|
||||
SdlAudioCDManager();
|
||||
virtual ~SdlAudioCDManager();
|
||||
|
||||
bool open() override;
|
||||
void close() override;
|
||||
bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate,
|
||||
Audio::Mixer::SoundType soundType) override;
|
||||
void stop() override;
|
||||
bool isPlaying() const override;
|
||||
void update() override;
|
||||
|
||||
protected:
|
||||
bool openCD(int drive) override;
|
||||
|
||||
SDL_CD *_cdrom;
|
||||
int _cdTrack, _cdNumLoops, _cdStartFrame, _cdDuration;
|
||||
uint32 _cdEndTime, _cdStopTime;
|
||||
};
|
||||
|
||||
#endif // !SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
|
||||
#endif
|
||||
415
backends/audiocd/win32/win32-audiocd.cpp
Normal file
415
backends/audiocd/win32/win32-audiocd.cpp
Normal file
@@ -0,0 +1,415 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Original license header:
|
||||
*
|
||||
* Cabal - Legacy Game Implementations
|
||||
*
|
||||
* Cabal is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#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 "backends/platform/sdl/win32/win32_wrapper.h"
|
||||
#include "common/array.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/fs.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/queue.h"
|
||||
#include "common/str.h"
|
||||
#include "common/timer.h"
|
||||
|
||||
#include <winioctl.h>
|
||||
#if defined(__MINGW32_VERSION)
|
||||
// Classic MinGW uses non standard paths for DDK headers.
|
||||
// __MINGW32_VERSION is not defined by the not classic Ming-w64
|
||||
#include <ddk/ntddcdrm.h>
|
||||
#else
|
||||
#include <ntddcdrm.h>
|
||||
#endif
|
||||
|
||||
class Win32AudioCDStream final : public AudioCDStream {
|
||||
public:
|
||||
Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry);
|
||||
~Win32AudioCDStream();
|
||||
|
||||
protected:
|
||||
uint getStartFrame() const override;
|
||||
uint getEndFrame() const override;
|
||||
bool readFrame(int frame, int16 *buffer) override;
|
||||
|
||||
private:
|
||||
HANDLE _driveHandle;
|
||||
const TRACK_DATA &_startEntry, &_endEntry;
|
||||
|
||||
enum {
|
||||
// The CD-ROM pre-gap is 2s
|
||||
kPreGapFrames = kFramesPerSecond * 2
|
||||
};
|
||||
|
||||
static int getFrameCount(const TRACK_DATA &data) {
|
||||
int time = data.Address[1];
|
||||
time *= kSecondsPerMinute;
|
||||
time += data.Address[2];
|
||||
time *= kFramesPerSecond;
|
||||
time += data.Address[3];
|
||||
return time;
|
||||
}
|
||||
};
|
||||
|
||||
Win32AudioCDStream::Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry) :
|
||||
_driveHandle(handle), _startEntry(startEntry), _endEntry(endEntry) {
|
||||
// We fill the buffer here already to prevent any out of sync issues due
|
||||
// to the CD not yet having spun up.
|
||||
startTimer(true);
|
||||
}
|
||||
|
||||
Win32AudioCDStream::~Win32AudioCDStream() {
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
uint Win32AudioCDStream::getStartFrame() const {
|
||||
return getFrameCount(_startEntry);
|
||||
}
|
||||
|
||||
uint Win32AudioCDStream::getEndFrame() const {
|
||||
return getFrameCount(_endEntry);
|
||||
}
|
||||
|
||||
bool Win32AudioCDStream::readFrame(int frame, int16 *buffer) {
|
||||
// Request to read that frame
|
||||
RAW_READ_INFO readAudio;
|
||||
memset(&readAudio, 0, sizeof(readAudio));
|
||||
readAudio.DiskOffset.QuadPart = (frame - kPreGapFrames) * 2048;
|
||||
readAudio.SectorCount = 1;
|
||||
readAudio.TrackMode = CDDA;
|
||||
|
||||
DWORD bytesReturned;
|
||||
return DeviceIoControl(
|
||||
_driveHandle,
|
||||
IOCTL_CDROM_RAW_READ,
|
||||
&readAudio,
|
||||
sizeof(readAudio),
|
||||
buffer,
|
||||
kBytesPerFrame,
|
||||
&bytesReturned,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
|
||||
class Win32AudioCDManager final : public DefaultAudioCDManager {
|
||||
public:
|
||||
Win32AudioCDManager();
|
||||
~Win32AudioCDManager();
|
||||
|
||||
bool open() override;
|
||||
void close() override;
|
||||
bool play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate,
|
||||
Audio::Mixer::SoundType soundType) override;
|
||||
bool isDataAndCDAudioReadFromSameCD() override;
|
||||
|
||||
protected:
|
||||
bool openCD(int drive) override;
|
||||
bool openCD(const Common::Path &drive) override;
|
||||
|
||||
private:
|
||||
bool loadTOC();
|
||||
|
||||
typedef Common::Array<char> DriveList;
|
||||
DriveList detectDrives();
|
||||
bool tryAddDrive(char drive, DriveList &drives);
|
||||
|
||||
HANDLE _driveHandle;
|
||||
int _firstTrack, _lastTrack;
|
||||
Common::Array<TRACK_DATA> _tocEntries;
|
||||
};
|
||||
|
||||
Win32AudioCDManager::Win32AudioCDManager() {
|
||||
_driveHandle = INVALID_HANDLE_VALUE;
|
||||
_firstTrack = _lastTrack = 0;
|
||||
}
|
||||
|
||||
Win32AudioCDManager::~Win32AudioCDManager() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool Win32AudioCDManager::open() {
|
||||
close();
|
||||
|
||||
if (openRealCD())
|
||||
return true;
|
||||
|
||||
return DefaultAudioCDManager::open();
|
||||
}
|
||||
|
||||
bool Win32AudioCDManager::openCD(int drive) {
|
||||
// Fetch the drive list
|
||||
DriveList drives = detectDrives();
|
||||
if (drive >= (int)drives.size())
|
||||
return false;
|
||||
|
||||
debug(1, "Opening CD drive %c:\\", drives[drive]);
|
||||
|
||||
// Construct the drive path and try to open it
|
||||
Common::String drivePath = Common::String::format("\\\\.\\%c:", drives[drive]);
|
||||
TCHAR *tDrivePath = Win32::stringToTchar(drivePath);
|
||||
_driveHandle = CreateFile(tDrivePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
free(tDrivePath);
|
||||
if (_driveHandle == INVALID_HANDLE_VALUE) {
|
||||
warning("Failed to open drive %c:\\, error %d", drives[drive], (int)GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!loadTOC()) {
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Win32AudioCDManager::openCD(const Common::Path &drive) {
|
||||
// Just some bounds checking
|
||||
if (drive.empty())
|
||||
return false;
|
||||
|
||||
Common::String driveS(drive.toString(Common::Path::kNativeSeparator));
|
||||
|
||||
if (driveS.size() > 3)
|
||||
return false;
|
||||
|
||||
if (!Common::isAlpha(driveS[0]) || driveS[1] != ':')
|
||||
return false;
|
||||
|
||||
if (driveS[2] != 0 && driveS[2] != '\\')
|
||||
return false;
|
||||
|
||||
DriveList drives;
|
||||
if (!tryAddDrive(toupper(driveS[0]), drives))
|
||||
return false;
|
||||
|
||||
// Construct the drive path and try to open it
|
||||
Common::String drivePath = Common::String::format("\\\\.\\%c:", drives[0]);
|
||||
TCHAR *tDrivePath = Win32::stringToTchar(drivePath);
|
||||
_driveHandle = CreateFile(tDrivePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
free(tDrivePath);
|
||||
if (_driveHandle == INVALID_HANDLE_VALUE) {
|
||||
warning("Failed to open drive %c:\\, error %d", drives[0], (int)GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!loadTOC()) {
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Win32AudioCDManager::close() {
|
||||
DefaultAudioCDManager::close();
|
||||
|
||||
if (_driveHandle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(_driveHandle);
|
||||
_driveHandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
_firstTrack = _lastTrack = 0;
|
||||
_tocEntries.clear();
|
||||
}
|
||||
|
||||
bool Win32AudioCDManager::play(int track, int numLoops, int startFrame, int duration, bool onlyEmulate,
|
||||
Audio::Mixer::SoundType soundType) {
|
||||
// Prefer emulation
|
||||
if (DefaultAudioCDManager::play(track, numLoops, startFrame, duration, onlyEmulate, soundType))
|
||||
return true;
|
||||
|
||||
// If we're set to only emulate, or have no CD drive, return here
|
||||
if (onlyEmulate || _driveHandle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
// HACK: For now, just assume that track number is right
|
||||
// That only works because ScummVM uses the wrong track number anyway
|
||||
|
||||
if (track >= (int)_tocEntries.size() - 1) {
|
||||
warning("No such track %d", track);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bail if the track isn't an audio track
|
||||
if ((_tocEntries[track].Control & 0x04) != 0) {
|
||||
warning("Track %d is not audio", track);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the AudioStream and play it
|
||||
debug(1, "Playing CD track %d", track);
|
||||
|
||||
Audio::SeekableAudioStream *audioStream = new Win32AudioCDStream(_driveHandle, _tocEntries[track], _tocEntries[track + 1]);
|
||||
|
||||
Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75);
|
||||
Audio::Timestamp end = (duration == 0) ? audioStream->getLength() : Audio::Timestamp(0, startFrame + duration, 75);
|
||||
|
||||
// Fake emulation since we're really playing an AudioStream
|
||||
_emulating = true;
|
||||
|
||||
_mixer->playStream(
|
||||
soundType,
|
||||
&_handle,
|
||||
Audio::makeLoopingAudioStream(audioStream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops),
|
||||
-1,
|
||||
_cd.volume,
|
||||
_cd.balance,
|
||||
DisposeAfterUse::YES,
|
||||
true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Win32AudioCDManager::loadTOC() {
|
||||
CDROM_READ_TOC_EX tocRequest;
|
||||
memset(&tocRequest, 0, sizeof(tocRequest));
|
||||
tocRequest.Format = CDROM_READ_TOC_EX_FORMAT_TOC;
|
||||
tocRequest.Msf = 1;
|
||||
tocRequest.SessionTrack = 0;
|
||||
|
||||
DWORD bytesReturned;
|
||||
CDROM_TOC tocData;
|
||||
bool result = DeviceIoControl(
|
||||
_driveHandle,
|
||||
IOCTL_CDROM_READ_TOC_EX,
|
||||
&tocRequest,
|
||||
sizeof(tocRequest),
|
||||
&tocData,
|
||||
sizeof(tocData),
|
||||
&bytesReturned,
|
||||
nullptr);
|
||||
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::Path gamePath = ConfMan.getPath("path");
|
||||
TCHAR *tGamePath = Win32::stringToTchar(gamePath.toString(Common::Path::kNativeSeparator));
|
||||
TCHAR fullPath[MAX_PATH];
|
||||
DWORD result = GetFullPathName(tGamePath, MAX_PATH, fullPath, nullptr);
|
||||
free(tGamePath);
|
||||
|
||||
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) {
|
||||
if (!Win32::isDriveCD(drive)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
debug(2, "Detected drive %c:\\ as a CD drive", drive);
|
||||
drives.push_back(drive);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Win32AudioCDManager::isDataAndCDAudioReadFromSameCD() {
|
||||
// It is a known bug under Windows that games that play CD audio cause
|
||||
// ScummVM to crash if the data files are read from the same CD.
|
||||
char driveLetter;
|
||||
Common::Path gameDataDir(ConfMan.getPath("path"));
|
||||
if (!gameDataDir.empty()) {
|
||||
driveLetter = gameDataDir.toString(Common::Path::kNativeSeparator)[0];
|
||||
} else {
|
||||
// That's it! I give up!
|
||||
Common::FSNode currentDir(".");
|
||||
gameDataDir = currentDir.getPath();
|
||||
if (!gameDataDir.empty()) {
|
||||
driveLetter = gameDataDir.toString(Common::Path::kNativeSeparator)[0];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return Win32::isDriveCD(driveLetter);
|
||||
}
|
||||
|
||||
AudioCDManager *createWin32AudioCDManager() {
|
||||
return new Win32AudioCDManager();
|
||||
}
|
||||
|
||||
#endif // WIN32
|
||||
58
backends/audiocd/win32/win32-audiocd.h
Normal file
58
backends/audiocd/win32/win32-audiocd.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Original license header:
|
||||
*
|
||||
* Cabal - Legacy Game Implementations
|
||||
*
|
||||
* Cabal is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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
|
||||
|
||||
Reference in New Issue
Block a user