Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
BACKENDS: Switch to a common base class for threaded audio CD streams
- Loading branch information
Matthew Hoops
authored and
Johannes Schickel
committed
Mar 13, 2016
1 parent
aa6ff44
commit 41a1dcb
Showing
5 changed files
with
316 additions
and
332 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
/* Cabal - Legacy Game Implementations | ||
* | ||
* Cabal is the legal property of its developers, whose names | ||
* are too numerous to list here. Please refer to the COPYRIGHT | ||
* file distributed with this source distribution. | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU General Public License | ||
* as published by the Free Software Foundation; either version 2 | ||
* of the License, or (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
* | ||
*/ | ||
|
||
#include "backends/audiocd/audiocd-stream.h" | ||
#include "common/textconsole.h" | ||
|
||
AudioCDStream::AudioCDStream() : _buffer(), _frame(0), _bufferPos(0), _bufferFrame(0), _forceStop(false) { | ||
} | ||
|
||
AudioCDStream::~AudioCDStream() { | ||
// Stop the timer; the subclass needs to do this, | ||
// so this is just a last resort. | ||
stopTimer(); | ||
|
||
// Clear any buffered frames | ||
emptyQueue(); | ||
} | ||
|
||
int AudioCDStream::readBuffer(int16 *buffer, const int numSamples) { | ||
int samples = 0; | ||
|
||
// See if any data is left first | ||
while (_bufferPos < kSamplesPerFrame && samples < numSamples) | ||
buffer[samples++] = _buffer[_bufferPos++]; | ||
|
||
// Bail out if done | ||
if (endOfData()) | ||
return samples; | ||
|
||
while (samples < numSamples && !endOfData()) { | ||
if (!readNextFrame()) | ||
return samples; | ||
|
||
// Copy the samples over | ||
for (_bufferPos = 0; _bufferPos < kSamplesPerFrame && samples < numSamples; ) | ||
buffer[samples++] = _buffer[_bufferPos++]; | ||
} | ||
|
||
return samples; | ||
} | ||
|
||
bool AudioCDStream::readNextFrame() { | ||
// Fetch a frame from the queue | ||
int16 *buffer; | ||
|
||
{ | ||
Common::StackLock lock(_mutex); | ||
|
||
// Nothing we can do if it's empty | ||
if (_bufferQueue.empty()) | ||
return false; | ||
|
||
buffer = _bufferQueue.pop(); | ||
} | ||
|
||
memcpy(_buffer, buffer, kSamplesPerFrame * 2); | ||
delete[] buffer; | ||
_frame++; | ||
return true; | ||
} | ||
|
||
bool AudioCDStream::endOfData() const { | ||
return !shouldForceStop() && getStartFrame() + _frame >= getEndFrame() && _bufferPos == kSamplesPerFrame; | ||
} | ||
|
||
bool AudioCDStream::seek(const Audio::Timestamp &where) { | ||
// Stop the timer | ||
stopTimer(); | ||
|
||
// Clear anything out of the queue | ||
emptyQueue(); | ||
|
||
// Convert to the frame number | ||
// Really not much else needed | ||
_bufferPos = kSamplesPerFrame; | ||
_frame = where.convertToFramerate(kFramesPerSecond).totalNumberOfFrames(); | ||
_bufferFrame = _frame; | ||
|
||
// Start the timer again | ||
startTimer(); | ||
return true; | ||
} | ||
|
||
Audio::Timestamp AudioCDStream::getLength() const { | ||
return Audio::Timestamp(0, getEndFrame() - getStartFrame(), kFramesPerSecond); | ||
} | ||
|
||
void AudioCDStream::timerProc(void *refCon) { | ||
static_cast<AudioCDStream *>(refCon)->onTimer(); | ||
} | ||
|
||
void AudioCDStream::onTimer() { | ||
// The goal here is to do as much work in this timer instead | ||
// of doing it in the readBuffer() call, which is the mixer. | ||
|
||
// If we're done, bail. | ||
if (shouldForceStop() || getStartFrame() + _bufferFrame >= getEndFrame()) | ||
return; | ||
|
||
// Get a quick count of the number of items in the queue | ||
// We don't care that much; we only need a quick estimate | ||
_mutex.lock(); | ||
uint32 queueCount = _bufferQueue.size(); | ||
_mutex.unlock(); | ||
|
||
// If we have enough audio buffered, bail out | ||
if (queueCount >= kBufferThreshold) | ||
return; | ||
|
||
while (!shouldForceStop() && queueCount < kBufferThreshold && getStartFrame() + _bufferFrame < getEndFrame()) { | ||
int16 *buffer = new int16[kSamplesPerFrame]; | ||
|
||
// Figure out the MSF of the frame we're looking for | ||
int frame = _bufferFrame + getStartFrame(); | ||
|
||
// Request to read that frame | ||
if (!readFrame(frame, buffer)) { | ||
warning("Failed to read CD audio"); | ||
forceStop(); | ||
return; | ||
} | ||
|
||
_bufferFrame++; | ||
|
||
// Now push the buffer onto the queue | ||
Common::StackLock lock(_mutex); | ||
_bufferQueue.push(buffer); | ||
queueCount = _bufferQueue.size(); | ||
} | ||
} | ||
|
||
void AudioCDStream::startTimer() { | ||
_forceStop = false; | ||
g_system->getTimerManager()->installTimerProc(timerProc, 10 * 1000, this, "AudioCDStream"); | ||
} | ||
|
||
void AudioCDStream::stopTimer() { | ||
forceStop(); | ||
g_system->getTimerManager()->removeTimerProc(timerProc); | ||
} | ||
|
||
void AudioCDStream::emptyQueue() { | ||
while (!_bufferQueue.empty()) | ||
delete[] _bufferQueue.pop(); | ||
} | ||
|
||
bool AudioCDStream::shouldForceStop() const { | ||
Common::StackLock lock(_forceStopMutex); | ||
return _forceStop; | ||
} | ||
|
||
void AudioCDStream::forceStop() { | ||
Common::StackLock lock(_forceStopMutex); | ||
_forceStop = true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
/* Cabal - Legacy Game Implementations | ||
* | ||
* Cabal is the legal property of its developers, whose names | ||
* are too numerous to list here. Please refer to the COPYRIGHT | ||
* file distributed with this source distribution. | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU General Public License | ||
* as published by the Free Software Foundation; either version 2 | ||
* of the License, or (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
* | ||
*/ | ||
|
||
#ifndef BACKENDS_AUDIOCD_AUDIOCD_STREAM_H | ||
#define BACKENDS_AUDIOCD_AUDIOCD_STREAM_H | ||
|
||
#include "audio/audiostream.h" | ||
#include "common/mutex.h" | ||
#include "common/queue.h" | ||
#include "common/timer.h" | ||
|
||
class AudioCDStream : public Audio::SeekableAudioStream { | ||
public: | ||
AudioCDStream(); | ||
~AudioCDStream(); | ||
|
||
int readBuffer(int16 *buffer, const int numSamples); | ||
bool isStereo() const { return true; } | ||
int getRate() const { return 44100; } | ||
bool endOfData() const; | ||
bool seek(const Audio::Timestamp &where); | ||
Audio::Timestamp getLength() const; | ||
|
||
protected: | ||
virtual uint getStartFrame() const = 0; | ||
virtual uint getEndFrame() const = 0; | ||
virtual bool readFrame(int frame, int16 *buffer) = 0; | ||
|
||
void startTimer(); | ||
void stopTimer(); | ||
|
||
enum { | ||
kBytesPerFrame = 2352, | ||
kSamplesPerFrame = kBytesPerFrame / 2 | ||
}; | ||
|
||
enum { | ||
kSecondsPerMinute = 60, | ||
kFramesPerSecond = 75 | ||
}; | ||
|
||
enum { | ||
// Keep about a second's worth of audio in the buffer | ||
kBufferThreshold = kFramesPerSecond | ||
}; | ||
|
||
private: | ||
int16 _buffer[kSamplesPerFrame]; | ||
int _frame; | ||
uint _bufferPos; | ||
|
||
Common::Queue<int16 *> _bufferQueue; | ||
int _bufferFrame; | ||
Common::Mutex _mutex; | ||
|
||
bool _forceStop; | ||
bool shouldForceStop() const; | ||
void forceStop(); | ||
Common::Mutex _forceStopMutex; | ||
|
||
bool readNextFrame(); | ||
static void timerProc(void *refCon); | ||
void onTimer(); | ||
void emptyQueue(); | ||
}; | ||
|
||
#endif |
Oops, something went wrong.