Add partial DAE support for compressed Redbook-compliant tracks
This commit is contained in:
parent
fdf103a111
commit
c132263116
2 changed files with 79 additions and 16 deletions
|
@ -40,6 +40,8 @@
|
|||
#define BYTES_PER_RAW_REDBOOK_FRAME 2352
|
||||
#define BYTES_PER_COOKED_REDBOOK_FRAME 2048
|
||||
#define REDBOOK_FRAMES_PER_SECOND 75
|
||||
#define REDBOOK_CHANNELS 2
|
||||
#define REDBOOK_PCM_FRAMES_PER_SECOND 44100
|
||||
#define MAX_REDBOOK_FRAMES 400000 // frames are Redbook's data unit
|
||||
#define MAX_REDBOOK_SECTOR 399999 // a sector is the index to a frame
|
||||
#define MAX_REDBOOK_TRACKS 99
|
||||
|
@ -127,8 +129,10 @@ private:
|
|||
TrackFile(Bit16u _chunkSize) : chunkSize(_chunkSize) {}
|
||||
public:
|
||||
virtual ~TrackFile() = default;
|
||||
virtual bool read(Bit8u *buffer, int seek, int count) = 0;
|
||||
virtual bool seek(Bit32u offset) = 0;
|
||||
virtual bool read(uint8_t *buffer,
|
||||
const uint32_t offset,
|
||||
const uint32_t requested_bytes) = 0;
|
||||
virtual bool seek(const uint32_t offset) = 0;
|
||||
virtual uint64_t decode(Bit16s *buffer, uint32_t desired_track_frames) = 0;
|
||||
virtual Bit16u getEndian() = 0;
|
||||
virtual Bit32u getRate() = 0;
|
||||
|
@ -146,8 +150,10 @@ private:
|
|||
BinaryFile (const BinaryFile&) = delete; // prevent copying
|
||||
BinaryFile& operator= (const BinaryFile&) = delete; // prevent assignment
|
||||
|
||||
bool read(Bit8u *buffer, int seek, int count);
|
||||
bool seek(Bit32u offset);
|
||||
bool read(uint8_t *buffer,
|
||||
const uint32_t offset,
|
||||
const uint32_t requested_bytes);
|
||||
bool seek(const uint32_t offset);
|
||||
uint64_t decode(Bit16s *buffer, Bit32u desired_track_frames);
|
||||
Bit16u getEndian();
|
||||
Bit32u getRate() { return 44100; }
|
||||
|
@ -166,12 +172,10 @@ private:
|
|||
AudioFile (const AudioFile&) = delete; // prevent copying
|
||||
AudioFile& operator= (const AudioFile&) = delete; // prevent assignment
|
||||
|
||||
bool read(Bit8u *buffer, int seek, int count) {
|
||||
(void)buffer; // unused but part of the API
|
||||
(void)seek; // ...
|
||||
(void)count; // ...
|
||||
return false; }
|
||||
bool seek(Bit32u offset);
|
||||
bool read(uint8_t *buffer,
|
||||
const uint32_t offset,
|
||||
const uint32_t requested_bytes);
|
||||
bool seek(const uint32_t offset);
|
||||
uint64_t decode(Bit16s *buffer, Bit32u desired_track_frames);
|
||||
Bit16u getEndian();
|
||||
Bit32u getRate();
|
||||
|
@ -179,6 +183,7 @@ private:
|
|||
int getLength();
|
||||
private:
|
||||
Sound_Sample *sample;
|
||||
uint32_t position;
|
||||
};
|
||||
|
||||
public:
|
||||
|
|
|
@ -71,14 +71,16 @@ CDROM_Interface_Image::BinaryFile::~BinaryFile()
|
|||
file = nullptr;
|
||||
}
|
||||
|
||||
bool CDROM_Interface_Image::BinaryFile::read(Bit8u *buffer, int seek, int count)
|
||||
bool CDROM_Interface_Image::BinaryFile::read(uint8_t *buffer,
|
||||
const uint32_t offset,
|
||||
const uint32_t requested_bytes)
|
||||
{
|
||||
// Guard: only proceed with a valid file
|
||||
if (file == nullptr)
|
||||
return false;
|
||||
|
||||
file->seekg(seek, ios::beg);
|
||||
file->read((char*)buffer, count);
|
||||
file->seekg(offset, ios::beg);
|
||||
file->read((char*)buffer, requested_bytes);
|
||||
return !file->fail();
|
||||
}
|
||||
|
||||
|
@ -107,7 +109,7 @@ Bit16u CDROM_Interface_Image::BinaryFile::getEndian()
|
|||
}
|
||||
|
||||
|
||||
bool CDROM_Interface_Image::BinaryFile::seek(Bit32u offset)
|
||||
bool CDROM_Interface_Image::BinaryFile::seek(const uint32_t offset)
|
||||
{
|
||||
// Guard: only proceed with a valid file
|
||||
if (file == nullptr)
|
||||
|
@ -129,7 +131,8 @@ uint64_t CDROM_Interface_Image::BinaryFile::decode(Bit16s *buffer, Bit32u desire
|
|||
|
||||
CDROM_Interface_Image::AudioFile::AudioFile(const char *filename, bool &error)
|
||||
: TrackFile(4096),
|
||||
sample(nullptr)
|
||||
sample(nullptr),
|
||||
position(0)
|
||||
{
|
||||
// Use the audio file's actual sample rate and number of channels as opposed to overriding
|
||||
Sound_AudioInfo desired = {AUDIO_S16, 0, 0};
|
||||
|
@ -159,7 +162,7 @@ CDROM_Interface_Image::AudioFile::~AudioFile()
|
|||
sample = nullptr;
|
||||
}
|
||||
|
||||
bool CDROM_Interface_Image::AudioFile::seek(Bit32u offset)
|
||||
bool CDROM_Interface_Image::AudioFile::seek(const uint32_t offset)
|
||||
{
|
||||
#ifdef BENCHMARK
|
||||
#include <ctime>
|
||||
|
@ -172,6 +175,9 @@ bool CDROM_Interface_Image::AudioFile::seek(Bit32u offset)
|
|||
#endif
|
||||
// Convert the byte-offset to a time offset (milliseconds)
|
||||
const bool result = Sound_Seek(sample, lround(offset / REDBOOK_PCM_BYTES_PER_MS));
|
||||
#ifdef DEBUG
|
||||
LOG_MSG("CDROM: seek to byte-offset %u", offset);
|
||||
#endif
|
||||
|
||||
#ifdef BENCHMARK
|
||||
const auto end = std::chrono::steady_clock::now();
|
||||
|
@ -182,6 +188,58 @@ bool CDROM_Interface_Image::AudioFile::seek(Bit32u offset)
|
|||
return result;
|
||||
}
|
||||
|
||||
bool CDROM_Interface_Image::AudioFile::read(uint8_t *buffer,
|
||||
const uint32_t offset,
|
||||
const uint32_t requested_bytes)
|
||||
{
|
||||
// Check for logic bugs
|
||||
assertm(buffer != nullptr, "buffer needs to be allocated but is the nullptr [Bug]");
|
||||
assertm(sample != nullptr, "Audio sample needs to be valid, but is the nullptr [Bug]");
|
||||
|
||||
// Guard again valid but no-op case
|
||||
if (requested_bytes == 0)
|
||||
return true;
|
||||
|
||||
// We support DAE from 16-bit, stereo, 44 kHz tracks. If the track doesn't conform to
|
||||
// this, then inform the user. Also, we allow up to 10 DAE-attempts before informing
|
||||
// the user - because some CD Player software will query this interface before using
|
||||
// CDROM-directed playback (not DAE), therefore we don't want to fail in those cases.
|
||||
if (getRate() != REDBOOK_PCM_FRAMES_PER_SECOND || getChannels() != REDBOOK_CHANNELS) {
|
||||
static uint8_t dae_attempts = 0;
|
||||
if (dae_attempts++ > 10) {
|
||||
E_Exit("\n"
|
||||
"CDROM: Digital Audio Extration (DAE) was attempted with a %s %u kHz\n"
|
||||
" track, but DAE is currently only compatible with stereo %u kHz\n"
|
||||
" tracks.",
|
||||
getChannels() == 2 ? "stereo" : "mono",
|
||||
getRate(),
|
||||
REDBOOK_PCM_FRAMES_PER_SECOND);
|
||||
}
|
||||
return false; // we always correctly return false to the application in this case.
|
||||
}
|
||||
|
||||
// Seek, but only if we have to
|
||||
if (position != (offset - requested_bytes))
|
||||
if (!seek(offset))
|
||||
return false;
|
||||
position = offset;
|
||||
|
||||
uint32_t decoded_frames = 0;
|
||||
const uint32_t requested_frames = requested_bytes / BYTES_PER_REDBOOK_PCM_FRAME;
|
||||
while (decoded_frames < requested_frames) {
|
||||
if (sample->flags & (SOUND_SAMPLEFLAG_ERROR | SOUND_SAMPLEFLAG_EOF))
|
||||
break;
|
||||
decoded_frames += Sound_Decode_Direct(sample,
|
||||
buffer + decoded_frames * BYTES_PER_REDBOOK_PCM_FRAME,
|
||||
requested_frames - decoded_frames);
|
||||
}
|
||||
if (decoded_frames < requested_frames)
|
||||
memset(buffer + decoded_frames * BYTES_PER_REDBOOK_PCM_FRAME,
|
||||
0,
|
||||
(requested_frames - decoded_frames) * BYTES_PER_REDBOOK_PCM_FRAME);
|
||||
return !(sample->flags & SOUND_SAMPLEFLAG_ERROR);
|
||||
}
|
||||
|
||||
uint64_t CDROM_Interface_Image::AudioFile::decode(Bit16s *buffer, Bit32u desired_track_frames)
|
||||
{
|
||||
return Sound_Decode_Direct(sample, (void*)buffer, desired_track_frames);
|
||||
|
|
Loading…
Add table
Reference in a new issue