Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DIRECTOR: add support for absolute timing CD audio playback via cuesheets #5428

Merged
merged 4 commits into from Nov 30, 2023

Conversation

mistydemeo
Copy link
Contributor

This adds support for playing CD audio via absolute timecodes instead of via track numbers. This is needed for several Director games. In order for this to work via CD audio emulation, we need access to a full table of contents for the disc. We can't just use the audio files for the CD tracks, because in almost every case, a data track comes before the first CD track - it's impossible to know what absolute sector an audio track begins and ends that without knowing the size of the data track.

To fix this, I've made use of cuesheets in order to have access to a full table of contents of the disc. In absolute playback mode, we check to see if the game directory has a file named disc.cue, and if so, we parse that and use it as the TOC. I'm using the new cuesheet parser that @sev- wrote. It's been updated with a few changes to accommodate this:

  • Several methods have been added to Common::CueSheet to allow retrieving tracks.
  • The parser has been reoriented so that the internal code keeps a flat array of tracks, instead of having tracks belong to files. This makes the track getter methods more convenient, and also eliminates a quirk where there were a few "fake" empty tracks inside file arrays.

The generic emulation-based CD audio player has been updated with a new playAbsolute method which takes an absolute timecode as its argument. At the moment, the macOS CD code doesn't implement playAbsolute; it's effectively a form of emulation already, which reads the AIFF files that macOS exposes instead of lower-level CDDA playback methods, and so we don't have access to the disc's TOC.

I've tested with Classical Cats for Mac. Results so far: it works great, and is playing back music as expected. There is a quirk where it's always playing from frame 0, but that's not the fault of the xlib - I haven't checked where the number is coming from, but it seems to be assigned in lingo outside the xlib.

Marking as draft because I haven't yet implemented true absolute playback support for the Windows or SDL CD-ROM devices. Also marking as a draft for feedback from @sev- on the changes to the cuesheet code.

@bluegr
Copy link
Member

bluegr commented Nov 14, 2023

Marking as draft because I haven't yet implemented true absolute playback support for the Windows or SDL CD-ROM devices.

But you can just add a stub for this in backends/audiocd/win32/win32-audiocd.cpp, like you did for Linux and MacOS, right?

@sev-
Copy link
Member

sev- commented Nov 17, 2023

Just in one of the programs I looked at, there was an example with a multitrack image with multiple files in each of them.

I have no prediction if we meet such a beast in the wild.

@mistydemeo
Copy link
Contributor Author

Just in one of the programs I looked at, there was an example with a multitrack image with multiple files in each of them.

As in, one cue with multiple files? That should be fine, though we'll want to make sure that the timing value we're parsing out is consistent between both kinds.

But you can just add a stub for this in backends/audiocd/win32/win32-audiocd.cpp, like you did for Linux and MacOS, right?

Yeah, definitely. I'll add that now while I wait to figure out a real implementation.

Copy link
Member

@sev- sev- left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your work.

I have several minor notes, but the most significant one, is that if I read the code correctly, on Linux and Windows, the cue sheets will not be used, because (a) they override the default method and (b) return false.

So, if I read the code correctly, I would drop those overrides in all backends and at this moment, have playAbsolute() work only with the CUE sheets, not talking to the real hardware, e.g. leave only the default implementation.

backends/audiocd/default/default-audiocd.cpp Show resolved Hide resolved
backends/audiocd/linux/linux-audiocd.cpp Outdated Show resolved Hide resolved
@@ -123,6 +123,12 @@ bool SdlAudioCDManager::play(int track, int numLoops, int startFrame, int durati
return true;
}

bool SdlAudioCDManager::playAbsolute(int startFrame, int numLoops, int duration, bool onlyEmulate,
Audio::Mixer::SoundType soundType) {
// TODO not implemented
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused with these. To me, it seems that they will override the default method, e.g. emulation, and will be no-op. Am I reading the code correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd gotten confused by the inheritance in a previous iteration and didn't realize I could safely omit playAbsolute implementations. I've deleted these now.

I checked the SDL docs and see how I think I could implement this method for real, but I also don't have access to a Linux machine to test.

@@ -249,17 +249,19 @@ void CueSheet::parseFilesContext(const char *line) {
int trackNum = atoi(nexttok(s, &s).c_str());
String trackType = nexttok(s, &s);

if (trackNum < 0 || (_currentTrack > 0 && _currentTrack + 1 != trackNum)) {
if (trackNum < 0 || (_currentTrack > 0 && _currentTrack + 2 != trackNum)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is + 2? Please add a comment above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment explaining it.

common/formats/cue.cpp Show resolved Hide resolved
common/formats/cue.cpp Outdated Show resolved Hide resolved
common/formats/cue.cpp Outdated Show resolved Hide resolved

// Not found within any tracks, but could be in the final track
if (frame > _tracks.back().indices.back()) {
debug(5, "CueSheet::getTrackAtFrame: Returning final track");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would turn it into a warning(), e.g. the user code is requesting us to do something odd, beyond the CD limits.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The request isn't as odd as it sounds - I'll update the comment to explain why.

The cuesheet gives us the starting indices of each track, but not track length or ending indices. In other words, we don't know how long the song is - we actually don't have an indication of whether the index the caller passed is in the disc or not, we just know that it comes after the start of this track.

The only actual way to 100% for sure know the number of sectors in the track is to actually check the file on disk and see how large it is. (Or load it as an audio file and check the duration.) That's a bit too complex to handle here; I'd rather defer complexity around loading and examining audio files to the CD audio player.

common/formats/cue.cpp Outdated Show resolved Hide resolved
@@ -22,11 +22,20 @@
#ifndef DIRECTOR_LINGO_XLIBS_APPLECDXOBJ_H
#define DIRECTOR_LINGO_XLIBS_APPLECDXOBJ_H

#include "common/formats/cue.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend forward declaration for Common::CueSheet instead of the whole #include it in the header.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@mistydemeo mistydemeo marked this pull request as ready for review November 23, 2023 06:10
@mistydemeo mistydemeo force-pushed the director_cue_sheets branch 3 times, most recently from 64cabae to a311459 Compare November 23, 2023 06:19
Copy link
Member

@sev- sev- left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! There is only a tiny nitpick in regards of code formatting

backends/audiocd/default/default-audiocd.h Outdated Show resolved Hide resolved
backends/audiocd/default/default-audiocd.cpp Outdated Show resolved Hide resolved
backends/audiocd/default/default-audiocd.cpp Show resolved Hide resolved
@sev-
Copy link
Member

sev- commented Nov 30, 2023

Thank you!

@sev- sev- merged commit bf233f0 into scummvm:master Nov 30, 2023
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants