disc: reworked track handling

getTrackBegin returns the first position of the track, whereas getTrackStart returns index1 of that track
That with other smaller bugfixes should play CDDA tracks from the beginning (it was 2 seconds into the track before).
Also, pregap wasn't handled properly.
This commit is contained in:
Jakub Czekański 2021-10-19 03:29:51 +02:00
parent 4307af04d3
commit fab6bd3881
17 changed files with 186 additions and 95 deletions

View file

@ -28,7 +28,13 @@ void CDROM::handleSector() {
if (q.validCrc()) {
this->lastQ = q;
}
readSector++;
if (audioStatus == AudioStatus::Forward) {
readSector += 5;
} else if (audioStatus == AudioStatus::Backward) {
readSector -= 5;
} else {
readSector++;
}
if (trackType == disc::TrackType::AUDIO && stat.play) {
if (memcmp(rawSector.data(), sync.data(), sync.size()) == 0) {
@ -82,12 +88,18 @@ void CDROM::handleSector() {
}
}
// Broken :( - gets triggered very soon after track starts playing
// in Ridge racer music preview.
if (mode.cddaAutoPause) {
if (track > previousTrack) {
postInterrupt(4);
writeResponse(stat._reg);
stat.play = false; // Pause
if (verbose) {
fmt::print("CDROM: CDDA end of track {}, auto pause\n", track);
}
}
}
previousTrack = track;
@ -298,8 +310,8 @@ void CDROM::handleCommand(uint8_t cmd) {
case 0x01: cmdGetstat(); break;
case 0x02: cmdSetloc(); break;
case 0x03: cmdPlay(); break;
// Missing 0x04 cmdForward
// Missing 0x05 cmdBackward
case 0x04: cmdForward(); break;
case 0x05: cmdBackward(); break;
case 0x06: cmdReadN(); break;
case 0x07: cmdMotorOn(); break;
case 0x08: cmdStop(); break;
@ -337,6 +349,7 @@ void CDROM::handleCommand(uint8_t cmd) {
postInterrupt(5);
writeResponse(0x11);
writeResponse(0x40);
if (verbose) fmt::print("CDROM: cmd{:02x} -> ({})\n", cmd, dumpFifo(interruptQueue.peek().response));
break;
}

View file

@ -11,6 +11,7 @@ namespace device {
namespace cdrom {
class CDROM {
enum class AudioStatus { Stop, Play, Pause, Forward, Backward };
union StatusCode {
enum class Mode { None, Reading, Seeking, Playing };
struct {
@ -129,12 +130,15 @@ class CDROM {
int seekSector = 0;
StatusCode stat;
AudioStatus audioStatus = AudioStatus::Stop;
int scexCounter = 0;
void cmdGetstat();
void cmdSetloc();
void cmdPlay();
void cmdForward();
void cmdBackward();
void cmdReadN();
void cmdMotorOn();
void cmdStop();
@ -234,6 +238,7 @@ class CDROM {
ar(seekSector);
ar(scexCounter);
ar(stat._reg);
ar(audioStatus);
ar(volumeLeftToLeft, volumeLeftToRight, volumeRightToLeft, volumeRightToRight);
ar(audio);
ar(rawSector);

View file

@ -31,30 +31,69 @@ void CDROM::cmdSetloc() {
}
void CDROM::cmdPlay() {
disc::Position pos;
if (CDROM_params.is_empty() || CDROM_params.peek() == 0) {
pos = disc::Position::fromLba(seekSector);
} else {
int track = bcd::toBinary(readParam()) - 1;
if (track >= (int)disc->getTrackCount()) {
pos = disc->getTrackStart(disc->getTrackCount() - 1);
} else {
pos = disc->getTrackStart(track);
}
if (verbose) fmt::print("CDROM: PLAY (track: {})\n", track + 1);
// Too many args
if (CDROM_params.size() > 1) {
postInterrupt(5);
writeResponse(0x01);
writeResponse(0x20);
return;
}
int trackNo = 0;
if (CDROM_params.size() == 1) {
trackNo = bcd::toBinary(readParam());
}
disc::Position pos = disc::Position::fromLba(readSector);
// Start playing track n
if (trackNo > 0) {
int track = std::min(trackNo - 1, (int)disc->getTrackCount() - 1);
pos = disc->getTrackStart(track);
if (verbose) fmt::print("CDROM: PLAY (track: {}, pos: {})\n", trackNo, pos.toString());
} else {
if (seekSector != 0) {
pos = disc::Position::fromLba(seekSector);
seekSector = 0;
}
if (audioStatus == AudioStatus::Pause || audioStatus == AudioStatus::Forward || audioStatus == AudioStatus::Backward) {
audioStatus = AudioStatus::Play;
} else if (audioStatus == AudioStatus::Play) {
} else if (audioStatus == AudioStatus::Stop) {
if (seekSector == 0) {
pos = disc->getTrackStart(0);
}
audioStatus = AudioStatus::Play;
}
if (verbose) fmt::print("CDROM: cmdPlay (pos: {})\n", pos.toString());
}
// Too many params - INT5 0x93 0x20
readSector = pos.toLba();
stat.setMode(StatusCode::Mode::Playing);
postInterrupt(3);
writeResponse(stat._reg);
previousTrack = disc->getTrackByPosition(pos);
}
if (verbose) fmt::print("CDROM: cmdPlay (pos: {})\n", pos.toString());
void CDROM::cmdForward() {
audioStatus = AudioStatus::Forward;
postInterrupt(3);
writeResponse(stat._reg);
if (verbose) fmt::print("CDROM: cmdForward -> 0x{:02x}\n", interruptQueue.peek().response[0]);
}
void CDROM::cmdBackward() {
audioStatus = AudioStatus::Backward;
postInterrupt(3);
writeResponse(stat._reg);
if (verbose) fmt::print("CDROM: cmdBackward -> 0x{:02x}\n", interruptQueue.peek().response[0]);
}
void CDROM::cmdReadN() {
@ -88,6 +127,7 @@ void CDROM::cmdMotorOn() {
void CDROM::cmdStop() {
stat.setMode(StatusCode::Mode::None);
audioStatus = AudioStatus::Stop;
stat.motor = 0;
postInterrupt(3);
@ -104,6 +144,7 @@ void CDROM::cmdPause() {
writeResponse(stat._reg);
stat.setMode(StatusCode::Mode::None);
audioStatus = AudioStatus::Pause;
postInterrupt(2);
writeResponse(stat._reg);
@ -188,10 +229,15 @@ void CDROM::cmdSetSession() {
void CDROM::cmdSeekP() {
readSector = seekSector;
seekSector = 0;
auto prevStat = stat;
stat.setMode(StatusCode::Mode::None);
postInterrupt(3);
writeResponse(stat._reg);
stat = prevStat;
postInterrupt(2, 500000);
writeResponse(stat._reg);
@ -357,7 +403,7 @@ void CDROM::cmdTest() {
if (verbose) {
auto CDROM_response = interruptQueue.peek().response;
fmt::print("//CDROM: cmdTest(0x{:02x}) -> ({})\n", opcode, dumpFifo(CDROM_response));
fmt::print("CDROM: cmdTest(0x{:02x}) -> ({})\n", opcode, dumpFifo(CDROM_response));
}
}

View file

@ -18,7 +18,8 @@ struct Disc {
virtual std::string getFile() const = 0;
virtual size_t getTrackCount() const = 0;
virtual int getTrackByPosition(Position pos) const = 0;
virtual Position getTrackStart(int track) const = 0;
virtual Position getTrackBegin(int track) const = 0; // MMSSFF of track first sector
virtual Position getTrackStart(int track) const = 0; // MMSSFF of track index1
virtual Position getTrackLength(int track) const = 0;
virtual Position getDiskSize() const = 0;

View file

@ -20,6 +20,11 @@ struct Empty : public Disc {
return 0;
}
Position getTrackBegin(int track) const {
(void)track;
return Position{0, 0, 0};
}
Position getTrackStart(int track) const {
(void)track;
return Position{0, 0, 0};

View file

@ -127,31 +127,26 @@ int Chd::getTrackByPosition(Position pos) const {
return 0;
}
Position Chd::getTrackStart(int track) const {
size_t frames = 0;
Position Chd::getTrackBegin(int track) const {
size_t frames = 75 * 2;
if ((unsigned)track < tracks.size()) {
for (int i = 0; i < track - 1; i++) {
for (int i = 0; i < track; i++) {
frames += tracks[i].frames;
}
}
return Position::fromLba(frames) + Position(0, 2, 0);
return Position::fromLba(frames);
}
Position Chd::getTrackStart(int track) const { return getTrackBegin(track) + tracks[track].start(); }
Position Chd::getTrackLength(int track) const {
if ((unsigned)track < tracks.size()) {
return Position::fromLba(tracks[track].frames);
} else {
return Position(0, 2, 0);
}
}
Position Chd::getTrackLength(int track) const { return Position::fromLba(tracks[track].pregap.toLba() + tracks[track].frames); }
Position Chd::getDiskSize() const {
size_t frames = 0;
for (size_t i = 0; i < tracks.size(); i++) {
frames += tracks[i].frames;
size_t frames = 75 * 2;
for (auto t : tracks) {
frames += t.pregap.toLba() + t.frames;
}
return Position::fromLba(frames) + Position(0, 2, 0);
return Position::fromLba(frames);
}
} // namespace format
} // namespace disc

View file

@ -18,6 +18,7 @@ struct Chd : public Disc {
std::string getFile() const override;
size_t getTrackCount() const override;
int getTrackByPosition(Position pos) const override;
Position getTrackBegin(int track) const override;
Position getTrackStart(int track) const override;
Position getTrackLength(int track) const override;
Position getDiskSize() const override;

View file

@ -6,34 +6,33 @@ namespace format {
std::string Cue::getFile() const { return file; }
Position Cue::getDiskSize() const {
int frames = 0;
size_t frames = 75 * 2;
for (auto t : tracks) {
frames += t.frames;
frames += t.pregap.toLba() + t.frames;
}
return Position::fromLba(frames) + Position{0, 2, 0};
return Position::fromLba(frames);
}
size_t Cue::getTrackCount() const { return tracks.size(); }
Position Cue::getTrackStart(int track) const {
size_t total = 0;
if (tracks.at(0).type == disc::TrackType::DATA) {
total += 75 * 2;
}
Position Cue::getTrackBegin(int track) const {
size_t total = 75 * 2;
for (int i = 0; i < track; i++) {
total += tracks.at(i).frames;
total += tracks[i].pregap.toLba() + tracks[i].frames;
}
return Position::fromLba(total);
}
Position Cue::getTrackLength(int track) const { return Position::fromLba(tracks.at(track).frames); }
Position Cue::getTrackStart(int track) const { return getTrackBegin(track) + tracks[track].start(); }
Position Cue::getTrackLength(int track) const { return Position::fromLba(tracks[track].pregap.toLba() + tracks[track].frames); }
int Cue::getTrackByPosition(Position pos) const {
for (size_t i = 0; i < getTrackCount(); i++) {
auto start = getTrackStart(i);
auto size = tracks[i].frames;
auto begin = getTrackBegin(i);
auto length = getTrackLength(i);
if (pos >= start && pos < start + Position::fromLba(size)) {
if (pos >= begin && pos < begin + length) {
return i;
}
}
@ -41,34 +40,34 @@ int Cue::getTrackByPosition(Position pos) const {
}
disc::Sector Cue::read(Position pos) {
auto buffer = std::vector<uint8_t>(Track::SECTOR_SIZE);
auto type = disc::TrackType::INVALID;
auto buffer = std::vector<uint8_t>(Track::SECTOR_SIZE, 0);
auto trackNum = getTrackByPosition(pos);
if (trackNum != -1) {
auto track = tracks[trackNum];
if (files.find(track.filename) == files.end()) {
auto f = unique_ptr_file(fopen(track.filename.c_str(), "rb"));
if (!f) {
fmt::print("Unable to load file {}\n", track.filename);
return std::make_pair(buffer, type);
}
files.emplace(track.filename, std::move(f));
}
type = track.type;
auto file = files[track.filename].get();
if (trackNum == 0 && type == disc::TrackType::DATA) {
pos = pos - Position{0, 2, 0};
}
auto seek = pos - *track.index0;
fseek(file, (long)(track.offset + seek.toLba() * Track::SECTOR_SIZE), SEEK_SET);
fread(buffer.data(), Track::SECTOR_SIZE, 1, file);
if (trackNum == -1) {
return std::make_pair(buffer, disc::TrackType::INVALID);
}
return std::make_pair(buffer, type);
auto track = tracks[trackNum];
if (files.find(track.filename) == files.end()) {
auto f = unique_ptr_file(fopen(track.filename.c_str(), "rb"));
if (!f) {
fmt::print("Unable to load file {}\n", track.filename);
return std::make_pair(buffer, disc::TrackType::INVALID);
}
files.emplace(track.filename, std::move(f));
}
auto file = files[track.filename].get();
auto seek = pos - (getTrackBegin(trackNum) + track.pregap);
long offset = track.offset + seek.toLba() * Track::SECTOR_SIZE;
if (offset < 0) { // Pregap
return std::make_pair(buffer, track.type);
}
fseek(file, offset, SEEK_SET);
fread(buffer.data(), Track::SECTOR_SIZE, 1, file);
return std::make_pair(buffer, track.type);
}
std::unique_ptr<Cue> Cue::fromBin(const char* file) {

View file

@ -22,6 +22,7 @@ struct Cue : public Disc {
std::string getFile() const override;
Position getDiskSize() const override;
size_t getTrackCount() const override;
Position getTrackBegin(int track) const override;
Position getTrackStart(int track) const override;
Position getTrackLength(int track) const override;
int getTrackByPosition(Position pos) const override;

View file

@ -82,18 +82,8 @@ void CueParser::addTrackToCue() {
// ignore if track is not completed
if (track.number == 0) return;
// if (track.number == 1 && track.type == Track::Type::DATA) {
// track.pregap = track.pregap + Position{0,2,0};
// }
if (track.pregap == Position{0, 0, 0} && track.index0) {
track.pregap = track.index1 - *track.index0;
} else {
track.index0 = track.index1;
}
if (!track.index0) {
track.index0 = track.index1 - track.pregap;
track.index0 = track.index1;
}
cue.tracks.push_back(track);

View file

@ -11,6 +11,8 @@ disc::Position Ecm::getDiskSize() const { return disc::Position::fromLba(data.si
size_t Ecm::getTrackCount() const { return 1; }
disc::Position Ecm::getTrackBegin(int track) const { return disc::Position(0, 2, 0); }
disc::Position Ecm::getTrackStart(int track) const { return disc::Position(0, 2, 0); }
disc::Position Ecm::getTrackLength(int track) const { return disc::Position::fromLba(data.size() / Track::SECTOR_SIZE); }

View file

@ -21,6 +21,7 @@ struct Ecm : public Disc {
std::string getFile() const override;
Position getDiskSize() const override;
size_t getTrackCount() const override;
Position getTrackBegin(int track) const override;
Position getTrackStart(int track) const override;
Position getTrackLength(int track) const override;
int getTrackByPosition(Position pos) const override;

View file

@ -13,7 +13,10 @@ Position Position::fromLba(size_t lba) {
return Position(mm, ss, ff);
}
std::string Position::toString() const { return fmt::format("{:02d}:{:02d}:{:02d}", mm, ss, ff); }
std::string Position::toString() const {
bool isNegative = mm < 0 || ss < 0 || ff < 0;
return fmt::format("{:02d}:{:02d}:{:02d}", std::abs(mm) * (isNegative ? -1 : 1), std::abs(ss), std::abs(ff));
}
int Position::toLba() const { return (mm * 60 * 75) + (ss * 75) + ff; }
@ -21,6 +24,22 @@ Position Position::operator+(const Position& p) const { return fromLba(toLba() +
Position Position::operator-(const Position& p) const { return fromLba(toLba() - p.toLba()); }
Position& Position::operator+=(const Position& p) {
auto val = fromLba(toLba() + p.toLba());
mm = val.mm;
ss = val.ss;
ff = val.ff;
return *this;
}
Position& Position::operator-=(const Position& p) {
auto val = fromLba(toLba() - p.toLba());
mm = val.mm;
ss = val.ss;
ff = val.ff;
return *this;
}
bool Position::operator==(const Position& p) const { return toLba() == p.toLba(); }
bool Position::operator>=(const Position& p) const { return toLba() >= p.toLba(); }

View file

@ -16,6 +16,8 @@ struct Position {
int toLba() const;
Position operator+(const Position& p) const;
Position operator-(const Position& p) const;
Position& operator+=(const Position& p);
Position& operator-=(const Position& p);
bool operator==(const Position& p) const;
bool operator>=(const Position& p) const;
bool operator<(const Position& p) const;

View file

@ -26,5 +26,14 @@ struct Track {
frames = 0;
}
Position pause() const {
if (index0) {
return (index1 - *index0);
}
return Position(0, 0, 0);
}
Position start() const { return pregap + pause(); }
};
} // namespace disc

View file

@ -21,21 +21,23 @@ void Cdrom::cdromWindow(System* sys) {
ImGui::Text("%s", cue->file.c_str());
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
ImGui::Text("Track Pregap Start End Offset Type File");
ImGui::Text("Track Pregap Pause Start End Length Offset Type File");
ImGuiListClipper clipper((int)cue->getTrackCount());
while (clipper.Step()) {
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
auto track = cue->tracks[i];
auto line = fmt::format("{:>5} {:<8} {:<8} {:<8} {:<9} {:<5} {}",
i + 1, //
POSITION(track.pregap), //
POSITION(track.index1), //
POSITION(track.index1 + Position::fromLba(track.frames)), //
track.offset, //
track.type == TrackType::DATA ? "DATA" : "AUDIO", //
getFilenameExt(track.filename) //
auto line = fmt::format("{:>5} {:<8} {:<8} {:<8} {:<8} {:<8} {:<9} {:<5} {}",
i + 1, //
POSITION(track.pregap), //
POSITION(track.pause()), //
POSITION(cue->getTrackStart(i)), //
POSITION(cue->getTrackStart(i) + cue->getTrackLength(i) - disc::Position(0, 0, 1)), //
POSITION(cue->getTrackLength(i)), //
track.offset, //
track.type == TrackType::DATA ? "DATA" : "AUDIO", //
getFilenameExt(track.filename) //
);
ImGui::Selectable(line.c_str());
@ -51,4 +53,4 @@ void Cdrom::cdromWindow(System* sys) {
void Cdrom::displayWindows(System* sys) {
if (cdromWindowOpen) cdromWindow(sys);
}
} // namespace gui::debug::cdrom
} // namespace gui::debug

View file

@ -20,7 +20,7 @@ namespace state {
const char* lastSaveName = "last.state";
struct StateMetadata {
inline static const uint32_t SAVESTATE_VERSION = 6;
inline static const uint32_t SAVESTATE_VERSION = 7;
uint32_t version = SAVESTATE_VERSION;
std::string biosPath;