Cuesheet

Enum Cuesheet 

Source
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.

BitsFieldMeaning
128×8catalog_numbermedia catalog number, in ASCII
64lead_in_samplesnumber of lead-in samples
1is_cddawhether cuesheet corresponds to CD-DA
7+258×8paddingall 0 bits
8track countnumber of cuesheet tracks
trackscuesheet 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: u64

Number 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: LeadOutCDDA

The 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

§catalog_number: Vec<Digit>

Media catalog number in ASCII digits

0 ≤ catalog number digits < 120

§tracks: Contiguous<254, TrackNonCDDA>

The cue sheet’s non-lead-out tracks

For Non-CD-DA, 0 ≤ track count ≤ 254

§lead_out: LeadOutNonCDDA

The required lead-out-track

This has a track number of 255 and indicates the end of the disc.

Implementations§

Source§

impl Cuesheet

Source

pub fn catalog_number(&self) -> impl Display

Media catalog number

Source

pub fn lead_in_samples(&self) -> Option<u64>

The number of lead-in samples for CD-DA discs

Source

pub fn is_cdda(&self) -> bool

If this is a CD-DA cuesheet

Source

pub fn track_count(&self) -> usize

Returns total number of tracks in cuesheet

Source

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

Source

pub fn display(&self, filename: &str) -> impl Display

Given a filename to use, returns cuesheet data as text

Source

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:19

INDEX points are in the format:

minutes      frames
     ↓↓      ↓↓
     MM::SS::FF
         ↑↑
    seconds

There 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());
Source

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());
Source

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

Trait Implementations§

Source§

impl AsBlockRef for Cuesheet

Source§

fn as_block_ref(&self) -> BlockRef<'_>

Returns fresh reference to ourself.
Source§

impl Clone for Cuesheet

Source§

fn clone(&self) -> Cuesheet

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Cuesheet

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<Cuesheet> for Block

Source§

fn from(b: Cuesheet) -> Self

Converts to this type from the input type.
Source§

impl FromBitStream for Cuesheet

Source§

type Error = Error

Error generated during parsing, such as io::Error
Source§

fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error>

Parse Self from reader
Source§

impl MetadataBlock for Cuesheet

Source§

const TYPE: BlockType = BlockType::Cuesheet

The metadata block’s type
Source§

const MULTIPLE: bool = true

Whether the block can occur multiple times in a file
Source§

fn bytes(&self) -> Option<BlockSize>

Size of block, in bytes, not including header
Source§

fn total_size(&self) -> Option<BlockSize>

Size of block, in bytes, including block header
Source§

impl OptionalMetadataBlock for Cuesheet

Source§

const OPTIONAL_TYPE: OptionalBlockType = OptionalBlockType::Cuesheet

Our optional block type
Source§

impl PartialEq for Cuesheet

Source§

fn eq(&self, other: &Cuesheet) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl ToBitStream for Cuesheet

Source§

type Error = Error

Error generated during building, such as io::Error
Source§

fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error>

Generate self to writer
Source§

fn bits<C>(&self) -> Result<C, Self::Error>
where C: Counter, Self: Sized,

Returns length of self in bits, if possible
Source§

fn bits_len<C, E>(&self) -> Result<C, Self::Error>
where C: Counter, E: Endianness, Self: Sized,

👎Deprecated since 4.0.0: use of bits() is preferred
Returns total length of self, if possible
Source§

impl TryFrom<Block> for Cuesheet

Source§

type Error = ()

The type returned in the event of a conversion error.
Source§

fn try_from(block: Block) -> Result<Self, ()>

Performs the conversion.
Source§

impl Eq for Cuesheet

Source§

impl PortableMetadataBlock for Cuesheet

Source§

impl StructuralPartialEq for Cuesheet

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.