From fab6bd38817a29d8d08dcc571765a7321b63e1f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Czeka=C5=84ski?= Date: Tue, 19 Oct 2021 03:29:51 +0200 Subject: [PATCH] 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. --- src/device/cdrom/cdrom.cpp | 19 +++++- src/device/cdrom/cdrom.h | 5 ++ src/device/cdrom/commands.cpp | 76 +++++++++++++++++++----- src/disc/disc.h | 3 +- src/disc/empty.h | 5 ++ src/disc/format/chd_format.cpp | 25 ++++---- src/disc/format/chd_format.h | 1 + src/disc/format/cue.cpp | 75 ++++++++++++----------- src/disc/format/cue.h | 1 + src/disc/format/cue_parser.cpp | 12 +--- src/disc/format/ecm.cpp | 2 + src/disc/format/ecm.h | 1 + src/disc/position.cpp | 21 ++++++- src/disc/position.h | 2 + src/disc/track.h | 9 +++ src/platform/windows/gui/debug/cdrom.cpp | 22 +++---- src/state/state.cpp | 2 +- 17 files changed, 186 insertions(+), 95 deletions(-) diff --git a/src/device/cdrom/cdrom.cpp b/src/device/cdrom/cdrom.cpp index c5d1f65..c4bbb5d 100644 --- a/src/device/cdrom/cdrom.cpp +++ b/src/device/cdrom/cdrom.cpp @@ -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; } diff --git a/src/device/cdrom/cdrom.h b/src/device/cdrom/cdrom.h index add83b7..b774066 100644 --- a/src/device/cdrom/cdrom.h +++ b/src/device/cdrom/cdrom.h @@ -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); diff --git a/src/device/cdrom/commands.cpp b/src/device/cdrom/commands.cpp index 7e7c52d..45a3b64 100644 --- a/src/device/cdrom/commands.cpp +++ b/src/device/cdrom/commands.cpp @@ -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)); } } diff --git a/src/disc/disc.h b/src/disc/disc.h index 194ca8a..8d88bd7 100644 --- a/src/disc/disc.h +++ b/src/disc/disc.h @@ -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; diff --git a/src/disc/empty.h b/src/disc/empty.h index f1120a7..b877f17 100644 --- a/src/disc/empty.h +++ b/src/disc/empty.h @@ -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}; diff --git a/src/disc/format/chd_format.cpp b/src/disc/format/chd_format.cpp index dfe4c81..2874b72 100644 --- a/src/disc/format/chd_format.cpp +++ b/src/disc/format/chd_format.cpp @@ -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 \ No newline at end of file diff --git a/src/disc/format/chd_format.h b/src/disc/format/chd_format.h index b82e7a4..95937f6 100644 --- a/src/disc/format/chd_format.h +++ b/src/disc/format/chd_format.h @@ -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; diff --git a/src/disc/format/cue.cpp b/src/disc/format/cue.cpp index 18a9faa..138274c 100644 --- a/src/disc/format/cue.cpp +++ b/src/disc/format/cue.cpp @@ -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(Track::SECTOR_SIZE); - auto type = disc::TrackType::INVALID; - + auto buffer = std::vector(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::fromBin(const char* file) { diff --git a/src/disc/format/cue.h b/src/disc/format/cue.h index 73c1caf..eecb19e 100644 --- a/src/disc/format/cue.h +++ b/src/disc/format/cue.h @@ -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; diff --git a/src/disc/format/cue_parser.cpp b/src/disc/format/cue_parser.cpp index 233e853..bd1d16c 100644 --- a/src/disc/format/cue_parser.cpp +++ b/src/disc/format/cue_parser.cpp @@ -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); diff --git a/src/disc/format/ecm.cpp b/src/disc/format/ecm.cpp index ba6d694..a054955 100644 --- a/src/disc/format/ecm.cpp +++ b/src/disc/format/ecm.cpp @@ -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); } diff --git a/src/disc/format/ecm.h b/src/disc/format/ecm.h index 20e045c..a6bb427 100644 --- a/src/disc/format/ecm.h +++ b/src/disc/format/ecm.h @@ -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; diff --git a/src/disc/position.cpp b/src/disc/position.cpp index 340a37d..e06b282 100644 --- a/src/disc/position.cpp +++ b/src/disc/position.cpp @@ -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(); } diff --git a/src/disc/position.h b/src/disc/position.h index 94ccfdc..54a10f3 100644 --- a/src/disc/position.h +++ b/src/disc/position.h @@ -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; diff --git a/src/disc/track.h b/src/disc/track.h index 6a34c24..30a01f4 100644 --- a/src/disc/track.h +++ b/src/disc/track.h @@ -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 diff --git a/src/platform/windows/gui/debug/cdrom.cpp b/src/platform/windows/gui/debug/cdrom.cpp index 2890537..ab583a2 100644 --- a/src/platform/windows/gui/debug/cdrom.cpp +++ b/src/platform/windows/gui/debug/cdrom.cpp @@ -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 \ No newline at end of file +} // namespace gui::debug \ No newline at end of file diff --git a/src/state/state.cpp b/src/state/state.cpp index 7ac38d9..e04fdf1 100644 --- a/src/state/state.cpp +++ b/src/state/state.cpp @@ -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;