{-|
Description:    Information about the division of a single disc into tracks.

Copyright:      (c) 2019-2021 Sam May
License:        GPL-3.0-or-later
Maintainer:     ag@eitilt.life

Stability:      stable
Portability:    non-portable (requires libcdio)

What many people would think of as the most important division on a CD isn't
quite as an integral a part of the physical data layout: they are described in
a table of contents before the first track of any recording session, and may
have lower-resolution "echoes" in the subchannels alongside the data, but it
would be hard to otherwise recover the track boundaries from the data itself.
This module, then, provides the functions to retrieve that metadata from the
table of contents and from the subchannels.
-}
module Sound.Libcdio.Track
    ( -- * Types
      Foreign.Track ( .. )
    , Foreign.minTrack
    , Foreign.maxTrack
    , Foreign.TrackNum
    , Foreign.TrackFormat ( .. )
      -- * Location
    , firstDiscTrack
    , lastDiscTrack
    , tracks
      -- ** Address
    , trackAt
    , pregap
    , offset
    , offsetEnd
    , totalLength
      -- * Data
    , format
    , isGreen
    , hasPreemphasis
    , copyPermit
    , quadAudio
    ) where


import qualified Foreign.Libcdio.Disc as Foreign
import qualified Foreign.Libcdio.Track as Foreign

import Sound.Libcdio.Read.Data
import Sound.Libcdio.Types.Cdio


-- | The number of the first track on the disc.  This will almost always be 1,
-- but that is not strictly guaranteed; the (perhaps theoretical) example is of
-- a multi-disc set, where later discs pick the numbering back up where the
-- previous one left off.
-- 
-- Returns 'Nothing' if the disc table of contents can't be read.
firstDiscTrack :: Cdio (Maybe Foreign.Track)
firstDiscTrack :: Cdio (Maybe Track)
firstDiscTrack = (Cdio -> IO (Maybe Track)) -> Cdio (Maybe Track)
forall a. (Cdio -> IO a) -> Cdio a
liftCdio Cdio -> IO (Maybe Track)
Foreign.firstTrackNum

-- | The number of the last track on the disc.
-- 
-- Returns 'Nothing' if the disc table of contents can't be read.
lastDiscTrack :: Cdio (Maybe Foreign.Track)
lastDiscTrack :: Cdio (Maybe Track)
lastDiscTrack = (Cdio -> IO (Maybe Track)) -> Cdio (Maybe Track)
forall a. (Cdio -> IO a) -> Cdio a
liftCdio Cdio -> IO (Maybe Track)
Foreign.lastTrackNum


-- | Get the number of tracks on a CD.
-- 
-- Returns 'Nothing' if the disc table of contents can't be read.
tracks :: Cdio (Maybe Foreign.Track)
tracks :: Cdio (Maybe Track)
tracks = (Cdio -> IO (Maybe Track)) -> Cdio (Maybe Track)
forall a. (Cdio -> IO a) -> Cdio a
liftCdio Cdio -> IO (Maybe Track)
Foreign.numTracks

-- | The track which contains the given address.
-- 
-- Returns 'Nothing' if the address is beyond the written data on the disc.
-- Note that 'Foreign.DiscLeadout' is treated as if it were a single sector
-- long:
-- 
-- >>> Just endTrack <- lastDiscTrack
-- >>> Just endAddr <- offsetEnd endTrack
-- >>> trackAt $ endAddr + 1
-- Just DiscLeadout
-- >>> trackAt $ endAddr + 2
-- Nothing
trackAt :: Lsn -> Cdio (Maybe Foreign.Track)
trackAt :: Lsn -> Cdio (Maybe Track)
trackAt = (Cdio -> IO (Maybe Track)) -> Cdio (Maybe Track)
forall a. (Cdio -> IO a) -> Cdio a
liftCdio ((Cdio -> IO (Maybe Track)) -> Cdio (Maybe Track))
-> (Lsn -> Cdio -> IO (Maybe Track)) -> Lsn -> Cdio (Maybe Track)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cdio -> Lsn -> IO (Maybe Track))
-> Lsn -> Cdio -> IO (Maybe Track)
forall a b c. (a -> b -> c) -> b -> a -> c
flip Cdio -> Lsn -> IO (Maybe Track)
Foreign.trackAt


-- | The address of the start of any pregap before the given track.
pregap :: Foreign.Track -> Cdio (Maybe Lsn)
pregap :: Track -> Cdio (Maybe Lsn)
pregap = (Cdio -> IO (Maybe Lsn)) -> Cdio (Maybe Lsn)
forall a. (Cdio -> IO a) -> Cdio a
liftCdio ((Cdio -> IO (Maybe Lsn)) -> Cdio (Maybe Lsn))
-> (Track -> Cdio -> IO (Maybe Lsn)) -> Track -> Cdio (Maybe Lsn)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cdio -> Track -> IO (Maybe Lsn))
-> Track -> Cdio -> IO (Maybe Lsn)
forall a b c. (a -> b -> c) -> b -> a -> c
flip Cdio -> Track -> IO (Maybe Lsn)
Foreign.pregapLsn

-- | The address of the start of the given track.
offset :: Foreign.Track -> Cdio (Maybe Lsn)
offset :: Track -> Cdio (Maybe Lsn)
offset = (Cdio -> IO (Maybe Lsn)) -> Cdio (Maybe Lsn)
forall a. (Cdio -> IO a) -> Cdio a
liftCdio ((Cdio -> IO (Maybe Lsn)) -> Cdio (Maybe Lsn))
-> (Track -> Cdio -> IO (Maybe Lsn)) -> Track -> Cdio (Maybe Lsn)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cdio -> Track -> IO (Maybe Lsn))
-> Track -> Cdio -> IO (Maybe Lsn)
forall a b c. (a -> b -> c) -> b -> a -> c
flip Cdio -> Track -> IO (Maybe Lsn)
Foreign.trackLsn

-- | The address of the end of the given track.
offsetEnd :: Foreign.Track -> Cdio (Maybe Lsn)
offsetEnd :: Track -> Cdio (Maybe Lsn)
offsetEnd = (Cdio -> IO (Maybe Lsn)) -> Cdio (Maybe Lsn)
forall a. (Cdio -> IO a) -> Cdio a
liftCdio ((Cdio -> IO (Maybe Lsn)) -> Cdio (Maybe Lsn))
-> (Track -> Cdio -> IO (Maybe Lsn)) -> Track -> Cdio (Maybe Lsn)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cdio -> Track -> IO (Maybe Lsn))
-> Track -> Cdio -> IO (Maybe Lsn)
forall a b c. (a -> b -> c) -> b -> a -> c
flip Cdio -> Track -> IO (Maybe Lsn)
Foreign.trackLastLsn

-- | The number of sectors "assigned" to the specified track, including any in
-- the pregap between it and the following one.
totalLength :: Foreign.Track -> Cdio (Maybe Word)
totalLength :: Track -> Cdio (Maybe Word)
totalLength = (Cdio -> IO (Maybe Word)) -> Cdio (Maybe Word)
forall a. (Cdio -> IO a) -> Cdio a
liftCdio ((Cdio -> IO (Maybe Word)) -> Cdio (Maybe Word))
-> (Track -> Cdio -> IO (Maybe Word)) -> Track -> Cdio (Maybe Word)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cdio -> Track -> IO (Maybe Word))
-> Track -> Cdio -> IO (Maybe Word)
forall a b c. (a -> b -> c) -> b -> a -> c
flip Cdio -> Track -> IO (Maybe Word)
Foreign.sectorCount


-- | The raw data structure of a track.
format :: Foreign.Track -> Cdio (Maybe Foreign.TrackFormat)
format :: Track -> Cdio (Maybe TrackFormat)
format = (Cdio -> IO (Maybe TrackFormat)) -> Cdio (Maybe TrackFormat)
forall a. (Cdio -> IO a) -> Cdio a
liftCdio ((Cdio -> IO (Maybe TrackFormat)) -> Cdio (Maybe TrackFormat))
-> (Track -> Cdio -> IO (Maybe TrackFormat))
-> Track
-> Cdio (Maybe TrackFormat)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cdio -> Track -> IO (Maybe TrackFormat))
-> Track -> Cdio -> IO (Maybe TrackFormat)
forall a b c. (a -> b -> c) -> b -> a -> c
flip Cdio -> Track -> IO (Maybe TrackFormat)
Foreign.trackFormat

-- | Whether the track data was stored using the Green Book (CD-i) standard.
isGreen :: Foreign.Track -> Cdio (Maybe Bool)
isGreen :: Track -> Cdio (Maybe Bool)
isGreen = (Cdio -> IO (Maybe Bool)) -> Cdio (Maybe Bool)
forall a. (Cdio -> IO a) -> Cdio a
liftCdio ((Cdio -> IO (Maybe Bool)) -> Cdio (Maybe Bool))
-> (Track -> Cdio -> IO (Maybe Bool)) -> Track -> Cdio (Maybe Bool)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cdio -> Track -> IO (Maybe Bool))
-> Track -> Cdio -> IO (Maybe Bool)
forall a b c. (a -> b -> c) -> b -> a -> c
flip Cdio -> Track -> IO (Maybe Bool)
Foreign.isGreen


-- | Whether preemphasis has been applied to the track for noise reduction.
hasPreemphasis :: Foreign.Track -> Cdio (Maybe Bool)
hasPreemphasis :: Track -> Cdio (Maybe Bool)
hasPreemphasis = (Cdio -> IO (Maybe Bool)) -> Cdio (Maybe Bool)
forall a. (Cdio -> IO a) -> Cdio a
liftCdio ((Cdio -> IO (Maybe Bool)) -> Cdio (Maybe Bool))
-> (Track -> Cdio -> IO (Maybe Bool)) -> Track -> Cdio (Maybe Bool)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cdio -> Track -> IO (Maybe Bool))
-> Track -> Cdio -> IO (Maybe Bool)
forall a b c. (a -> b -> c) -> b -> a -> c
flip Cdio -> Track -> IO (Maybe Bool)
Foreign.hasPreemphasis

-- | Whether the track may be legally copied.
copyPermit :: Foreign.Track -> Cdio (Maybe Bool)
copyPermit :: Track -> Cdio (Maybe Bool)
copyPermit = (Cdio -> IO (Maybe Bool)) -> Cdio (Maybe Bool)
forall a. (Cdio -> IO a) -> Cdio a
liftCdio ((Cdio -> IO (Maybe Bool)) -> Cdio (Maybe Bool))
-> (Track -> Cdio -> IO (Maybe Bool)) -> Track -> Cdio (Maybe Bool)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cdio -> Track -> IO (Maybe Bool))
-> Track -> Cdio -> IO (Maybe Bool)
forall a b c. (a -> b -> c) -> b -> a -> c
flip Cdio -> Track -> IO (Maybe Bool)
Foreign.copyPermit

-- | Whether the track was encoded with a richer 4-channel structure, or
-- whether it just includes the standard stereo (2-channel) layout.
quadAudio :: Foreign.Track -> Cdio (Maybe Bool)
quadAudio :: Track -> Cdio (Maybe Bool)
quadAudio Track
t = (Cdio -> IO (Maybe Bool)) -> Cdio (Maybe Bool)
forall a. (Cdio -> IO a) -> Cdio a
liftCdio ((Cdio -> IO (Maybe Bool)) -> Cdio (Maybe Bool))
-> (Cdio -> IO (Maybe Bool)) -> Cdio (Maybe Bool)
forall a b. (a -> b) -> a -> b
$ \Cdio
c -> (Word -> Bool) -> Maybe Word -> Maybe Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
> Word
2) (Maybe Word -> Maybe Bool) -> IO (Maybe Word) -> IO (Maybe Bool)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Cdio -> Track -> IO (Maybe Word)
Foreign.numChannels Cdio
c Track
t