pub enum Cuesheet {
CDDA {
catalog_number: Option<[Digit; 13]>,
lead_in_samples: u64,
tracks: Contiguous<99, TrackCDDA>,
lead_out: LeadOutCDDA,
},
NonCDDA {
catalog_number: Vec<Digit>,
tracks: Contiguous<254, TrackNonCDDA>,
lead_out: LeadOutNonCDDA,
},
}Expand description
A CUESHEET metadata block
A cue sheet stores a disc’s original layout with all its tracks, index points, and disc-specific metadata.
This block may occur multiple times in a FLAC file, theoretically.
| Bits | Field | Meaning |
|---|---|---|
| 128×8 | catalog_number | media catalog number, in ASCII |
| 64 | lead_in_samples | number of lead-in samples |
| 1 | is_cdda | whether cuesheet corresponds to CD-DA |
| 7+258×8 | padding | all 0 bits |
| 8 | track count | number of cuesheet tracks |
tracks | cuesheet track₀, cuesheet track₁, … |
Although the structure of this block is not particularly complicated, a CUESHEET block must abide by many rules in order to be considered valid. Many of these rules are encoded into the type system.
Variants§
CDDA
A CD-DA Cuesheet, for audio CDs
Fields
catalog_number: Option<[Digit; 13]>Media catalog number in ASCII digits
For CD-DA, if present, this number must be exactly 13 ASCII digits followed by all 0 bytes.
lead_in_samples: u64Number of lead-in samples
For CD-DA, this must be at least 2 seconds (88200 samples), but may be longer.
Non-CD-DA cuesheets must always use 0 for lead-in samples, which is why that variant does not have this field.
tracks: Contiguous<99, TrackCDDA>The cue sheet’s non-lead-out tracks
For CD-DA, 0 ≤ track count ≤ 99
lead_out: LeadOutCDDAThe required lead-out-track
This has a track number of 170 and indicates the end of the disc.
NonCDDA
A Non-CD-DA Cuesheet, for non-audio CDs
Fields
tracks: Contiguous<254, TrackNonCDDA>The cue sheet’s non-lead-out tracks
For Non-CD-DA, 0 ≤ track count ≤ 254
lead_out: LeadOutNonCDDAThe required lead-out-track
This has a track number of 255 and indicates the end of the disc.
Implementations§
Source§impl Cuesheet
impl Cuesheet
Sourcepub fn catalog_number(&self) -> impl Display
pub fn catalog_number(&self) -> impl Display
Media catalog number
Sourcepub fn lead_in_samples(&self) -> Option<u64>
pub fn lead_in_samples(&self) -> Option<u64>
The number of lead-in samples for CD-DA discs
Sourcepub fn track_count(&self) -> usize
pub fn track_count(&self) -> usize
Returns total number of tracks in cuesheet
Sourcepub fn tracks(&self) -> Box<dyn Iterator<Item = TrackGeneric> + '_>
pub fn tracks(&self) -> Box<dyn Iterator<Item = TrackGeneric> + '_>
Iterates over all tracks in cuesheet
Tracks are converted into a unified format suitable for display
Sourcepub fn display(&self, filename: &str) -> impl Display
pub fn display(&self, filename: &str) -> impl Display
Given a filename to use, returns cuesheet data as text
Sourcepub fn parse(total_samples: u64, cuesheet: &str) -> Result<Self, CuesheetError>
pub fn parse(total_samples: u64, cuesheet: &str) -> Result<Self, CuesheetError>
Attempts to parse new Cuesheet from cue sheet file
total_samples should be the total number
of channel-independent samples, used to
calculate the lead-out track
cuesheet is the entire cuesheet as a string slice
This is a simplistic cuesheet parser sufficient for generating FLAC-compatible CUESHEET metadata blocks.
§Example File
FILE "cdimage.wav" WAVE
TRACK 01 AUDIO
INDEX 01 00:00:00
TRACK 02 AUDIO
INDEX 00 02:57:52
INDEX 01 03:00:02
TRACK 03 AUDIO
INDEX 00 04:46:17
INDEX 01 04:48:64
TRACK 04 AUDIO
INDEX 00 07:09:01
INDEX 01 07:11:49
TRACK 05 AUDIO
INDEX 00 09:11:47
INDEX 01 09:13:54
TRACK 06 AUDIO
INDEX 00 11:10:13
INDEX 01 11:12:51
TRACK 07 AUDIO
INDEX 00 13:03:74
INDEX 01 13:07:19INDEX points are in the format:
minutes frames
↓↓ ↓↓
MM::SS::FF
↑↑
secondsThere are 75 frames per second, and 60 seconds per minute. Since CD audio has 44100 channel-independent samples per second, the number of channel-independent samples per frame is 588 (44100 ÷ 75 = 588).
Thus, the sample offset of each INDEX point can be calculated like:
samples = ((MM × 60 × 75) + (SS × 75) + FF) × 588
Note that the INDEX points are stored in increasing order,
as a standard single file cue sheet.
§Example
use flac_codec::metadata::{Cuesheet, cuesheet::{Track, Index, ISRC}};
let file = "FILE \"cdimage.wav\" WAVE
TRACK 01 AUDIO
INDEX 01 00:00:00
TRACK 02 AUDIO
INDEX 00 02:57:52
INDEX 01 03:00:02
TRACK 03 AUDIO
INDEX 00 04:46:17
INDEX 01 04:48:64
TRACK 04 AUDIO
INDEX 00 07:09:01
INDEX 01 07:11:49
TRACK 05 AUDIO
INDEX 00 09:11:47
INDEX 01 09:13:54
TRACK 06 AUDIO
INDEX 00 11:10:13
INDEX 01 11:12:51
TRACK 07 AUDIO
INDEX 00 13:03:74
INDEX 01 13:07:19
";
let cuesheet = Cuesheet::parse(39731748, file).unwrap();
assert!(cuesheet.is_cdda());
let mut tracks = cuesheet.tracks();
assert_eq!(
tracks.next(),
Some(Track {
offset: 0,
number: Some(01),
isrc: ISRC::None,
non_audio: false,
pre_emphasis: false,
index_points: vec![
Index { number: 01, offset: 0 },
],
}),
);
assert_eq!(
tracks.next(),
Some(Track {
// track's offset is that of its first index point
offset: ((2 * 60 * 75) + (57 * 75) + 52) * 588,
number: Some(02),
isrc: ISRC::None,
non_audio: false,
pre_emphasis: false,
index_points: vec![
// index point offsets are stored relative
// to the track's offset
Index { number: 00, offset: 0 },
Index { number: 01, offset: 175 * 588 }
],
}),
);
assert_eq!(
tracks.next(),
Some(Track {
offset: ((4 * 60 * 75) + (46 * 75) + 17) * 588,
number: Some(03),
isrc: ISRC::None,
non_audio: false,
pre_emphasis: false,
index_points: vec![
Index { number: 00, offset: 0 },
Index { number: 01, offset: 197 * 588 }
],
}),
);
// skip over some tracks for brevity
assert_eq!(tracks.next().and_then(|track| track.number), Some(04));
assert_eq!(tracks.next().and_then(|track| track.number), Some(05));
assert_eq!(tracks.next().and_then(|track| track.number), Some(06));
assert_eq!(tracks.next().and_then(|track| track.number), Some(07));
// the final lead-out track has an offset of the stream's total samples
// and no index points
assert_eq!(
tracks.next(),
Some(Track {
offset: 39731748,
number: None,
isrc: ISRC::None,
non_audio: false,
pre_emphasis: false,
index_points: vec![],
}),
);
assert!(tracks.next().is_none());Sourcepub fn track_sample_ranges(&self) -> impl Iterator<Item = Range<u64>>
pub fn track_sample_ranges(&self) -> impl Iterator<Item = Range<u64>>
Iterates over track ranges in channel-indepedent samples
Note that the range of each track is from the track
start to the start of the next track, which is indicated
by INDEX 01.
It is not from the start of the pre-gaps (INDEX 00),
which may not be present.
use flac_codec::metadata::Cuesheet;
let file = "FILE \"cdimage.wav\" WAVE
TRACK 01 AUDIO
INDEX 01 00:00:00
TRACK 02 AUDIO
INDEX 00 02:57:52
INDEX 01 03:00:02
TRACK 03 AUDIO
INDEX 00 04:46:17
INDEX 01 04:48:64
TRACK 04 AUDIO
INDEX 00 07:09:01
INDEX 01 07:11:49
TRACK 05 AUDIO
INDEX 00 09:11:47
INDEX 01 09:13:54
TRACK 06 AUDIO
INDEX 00 11:10:13
INDEX 01 11:12:51
TRACK 07 AUDIO
INDEX 00 13:03:74
INDEX 01 13:07:19
";
let cuesheet = Cuesheet::parse(39731748, file).unwrap();
let mut track_ranges = cuesheet.track_sample_ranges();
// 00:00:00 to 03:00:02
assert_eq!(
track_ranges.next(),
Some(0..((3 * 60 * 75) + (0 * 75) + 2) * 588)
);
// 03:00:02 to 04:48:64
assert_eq!(
track_ranges.next(),
Some(((3 * 60 * 75) + (0 * 75) + 2) * 588..((4 * 60 * 75) + (48 * 75) + 64) * 588),
);
// skip a few tracks for brevity
assert!(track_ranges.next().is_some()); // to 07:11.49
assert!(track_ranges.next().is_some()); // to 09:13:54
assert!(track_ranges.next().is_some()); // to 11:12:51
assert!(track_ranges.next().is_some()); // to 13:07:19
// 13:07:19 to the lead-out
assert_eq!(
track_ranges.next(),
Some(((13 * 60 * 75) + (7 * 75) + 19) * 588..39731748),
);
assert!(track_ranges.next().is_none());Sourcepub fn track_byte_ranges(
&self,
channel_count: u8,
bits_per_sample: u32,
) -> impl Iterator<Item = Range<u64>>
pub fn track_byte_ranges( &self, channel_count: u8, bits_per_sample: u32, ) -> impl Iterator<Item = Range<u64>>
Iterates over track ranges in bytes
Much like Cuesheet::track_sample_ranges, but takes
a channel count and bits-per-sample to convert the ranges to bytes.
For CD-DA, those values are 2 and 16, respectively.
§Panics
Panics if either channel_count or bits_per_sample are 0