aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backends/audiocd/win32/win32-audiocd.cpp154
1 files changed, 122 insertions, 32 deletions
diff --git a/backends/audiocd/win32/win32-audiocd.cpp b/backends/audiocd/win32/win32-audiocd.cpp
index fdde61cc45..6dd2b756a9 100644
--- a/backends/audiocd/win32/win32-audiocd.cpp
+++ b/backends/audiocd/win32/win32-audiocd.cpp
@@ -26,9 +26,11 @@
#include "backends/audiocd/default/default-audiocd.h"
#include "common/array.h"
#include "common/config-manager.h"
-#include "common/endian.h"
-#include "common/str.h"
#include "common/debug.h"
+#include "common/mutex.h"
+#include "common/queue.h"
+#include "common/str.h"
+#include "common/timer.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
@@ -59,6 +61,11 @@ enum {
kPreGapFrames = kFramesPerSecond * 2
};
+enum {
+ // Keep about a second's worth of audio in the buffer
+ kBufferThreshold = kFramesPerSecond
+};
+
static int getFrameCount(const TRACK_DATA &data) {
int time = data.Address[1];
time *= kSecondsPerMinute;
@@ -71,6 +78,7 @@ static int getFrameCount(const TRACK_DATA &data) {
class Win32AudioCDStream : public Audio::SeekableAudioStream {
public:
Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry);
+ ~Win32AudioCDStream();
int readBuffer(int16 *buffer, const int numSamples);
bool isStereo() const { return true; }
@@ -86,15 +94,26 @@ private:
int _frame;
uint _bufferPos;
+ Common::Queue<int16 *> _bufferQueue;
+ int _bufferFrame;
+ Common::Mutex _mutex;
+
bool readNextFrame();
+ static void timerProc(void *refCon);
+ void onTimer();
+ void emptyQueue();
+ void startTimer();
+ void stopTimer();
};
Win32AudioCDStream::Win32AudioCDStream(HANDLE handle, const TRACK_DATA &startEntry, const TRACK_DATA &endEntry) :
- _driveHandle(handle), _startEntry(startEntry), _endEntry(endEntry), _buffer(), _frame(0), _bufferPos(kSamplesPerFrame) {
- // Read the first frame here. This should hopefully keep games more
- // in sync instead of potentially waiting in the mixer thread for the
- // disc to seek to the first frame.
- readNextFrame();
+ _driveHandle(handle), _startEntry(startEntry), _endEntry(endEntry), _buffer(), _frame(0), _bufferPos(kSamplesPerFrame), _bufferFrame(0) {
+ startTimer();
+}
+
+Win32AudioCDStream::~Win32AudioCDStream() {
+ stopTimer();
+ emptyQueue();
}
int Win32AudioCDStream::readBuffer(int16 *buffer, const int numSamples) {
@@ -121,33 +140,21 @@ int Win32AudioCDStream::readBuffer(int16 *buffer, const int numSamples) {
}
bool Win32AudioCDStream::readNextFrame() {
- // Figure out the MSF of the frame we're looking for
- int frame = _frame + getFrameCount(_startEntry);
-
- // Request to read that frame. Subtract the pre-gap frames
- // so that we get to the right sector.
- RAW_READ_INFO readAudio;
- memset(&readAudio, 0, sizeof(readAudio));
- readAudio.DiskOffset.QuadPart = (frame - kPreGapFrames) * 2048;
- readAudio.SectorCount = 1;
- readAudio.TrackMode = CDDA;
-
- DWORD bytesReturned;
- bool result = DeviceIoControl(
- _driveHandle,
- IOCTL_CDROM_RAW_READ,
- &readAudio,
- sizeof(readAudio),
- &_buffer,
- sizeof(_buffer),
- &bytesReturned,
- NULL);
- if (!result) {
- warning("Failed to retrieve CD sector %d: %d", frame, (int)GetLastError());
- _frame = getFrameCount(_endEntry);
- return false;
+ // Fetch a frame from the queue
+ int16 *buffer;
+
+ {
+ Common::StackLock lock(_mutex);
+
+ // Nothing we can do if it's empty
+ if (_bufferQueue.empty())
+ return false;
+
+ buffer = _bufferQueue.pop();
}
+ memcpy(_buffer, buffer, kSamplesPerFrame * 2);
+ delete[] buffer;
_frame++;
return true;
}
@@ -157,10 +164,20 @@ bool Win32AudioCDStream::endOfData() const {
}
bool Win32AudioCDStream::seek(const Audio::Timestamp &where) {
+ // Stop the timer
+ stopTimer();
+
+ // Clear anything out of the queue
+ emptyQueue();
+
// Convert to the frame number
// Really not much else needed
_bufferPos = kSamplesPerFrame;
_frame = where.convertToFramerate(kFramesPerSecond).totalNumberOfFrames();
+ _bufferFrame = _frame;
+
+ // Start the timer again
+ startTimer();
return true;
}
@@ -168,6 +185,79 @@ Audio::Timestamp Win32AudioCDStream::getLength() const {
return Audio::Timestamp(0, getFrameCount(_endEntry) - getFrameCount(_startEntry), 75);
}
+void Win32AudioCDStream::timerProc(void *refCon) {
+ static_cast<Win32AudioCDStream *>(refCon)->onTimer();
+}
+
+void Win32AudioCDStream::onTimer() {
+ // The goal here is to do as much work in this timer instead
+ // of doing it in the readBuffer() call, which is the mixer.
+
+ // If we're done, bail.
+ if (getFrameCount(_startEntry) + _bufferFrame >= getFrameCount(_endEntry))
+ return;
+
+ // Get a quick count of the number of items in the queue
+ // We don't care that much; we only need a quick estimate
+ _mutex.lock();
+ uint32 queueCount = _bufferQueue.size();
+ _mutex.unlock();
+
+ // If we have enough audio buffered, bail out
+ if (queueCount >= kBufferThreshold)
+ return;
+
+ while (queueCount < kBufferThreshold && getFrameCount(_startEntry) + _bufferFrame < getFrameCount(_endEntry)) {
+ int16 *buffer = new int16[kSamplesPerFrame];
+
+ // Figure out the MSF of the frame we're looking for
+ int frame = _bufferFrame + getFrameCount(_startEntry);
+
+ // Request to read that frame
+ RAW_READ_INFO readAudio;
+ memset(&readAudio, 0, sizeof(readAudio));
+ readAudio.DiskOffset.QuadPart = (frame - kPreGapFrames) * 2048;
+ readAudio.SectorCount = 1;
+ readAudio.TrackMode = CDDA;
+
+ DWORD bytesReturned;
+ bool result = DeviceIoControl(
+ _driveHandle,
+ IOCTL_CDROM_RAW_READ,
+ &readAudio,
+ sizeof(readAudio),
+ buffer,
+ kBytesPerFrame,
+ &bytesReturned,
+ NULL);
+ if (!result) {
+ warning("Failed to retrieve CD sector %d: %d", frame, (int)GetLastError());
+ _bufferFrame = getFrameCount(_endEntry) - getFrameCount(_startEntry);
+ return;
+ }
+
+ _bufferFrame++;
+
+ // Now push the buffer onto the queue
+ Common::StackLock lock(_mutex);
+ _bufferQueue.push(buffer);
+ queueCount = _bufferQueue.size();
+ }
+}
+
+void Win32AudioCDStream::startTimer() {
+ g_system->getTimerManager()->installTimerProc(timerProc, 10 * 1000, this, "Win32AudioCDStream");
+}
+
+void Win32AudioCDStream::stopTimer() {
+ g_system->getTimerManager()->removeTimerProc(timerProc);
+}
+
+void Win32AudioCDStream::emptyQueue() {
+ while (!_bufferQueue.empty())
+ delete[] _bufferQueue.pop();
+}
+
class Win32AudioCDManager : public DefaultAudioCDManager {
public:
Win32AudioCDManager();