-- | -- Module : Text.CueSheet.Render -- Copyright : © 2016–2019 Mark Karpov -- License : BSD 3 clause -- -- Maintainer : Mark Karpov -- Stability : experimental -- Portability : portable -- -- The module contains a CUE sheet render. You probably want to import -- "Text.CueSheet" instead. {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} module Text.CueSheet.Render ( renderCueSheet ) where import Control.Monad import Control.Monad.State import Control.Monad.Writer.Lazy import Data.Char (isSpace, chr) import Data.Maybe (catMaybes) import Data.Text (Text) import Numeric.Natural import Text.CueSheet.Types import Text.Printf (printf) import qualified Data.ByteString as B import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy as BL import qualified Data.List.NonEmpty as NE import qualified Data.Text as T import qualified Data.Text.Encoding as T -- | Render a CUE sheet as a lazy 'BL.ByteString'. All 'Text' values in the -- 'CueSheet' will be UTF-8 encoded. renderCueSheet :: Bool -- ^ Use CRLF sequence as “end of line” separator -> CueSheet -- ^ The 'CueSheet' to render -> BL.ByteString -- ^ The result renderCueSheet csrf CueSheet {..} = BB.toLazyByteString . execWriter . flip evalStateT (max cueFirstTrackNumber 1) $ do let eol = tell (if csrf then "\r\n" else "\n") tellText x = let raw = T.encodeUtf8 x in tell . BB.byteString $ if B.any (isSpace . chr . fromIntegral) raw || B.null raw then "\"" <> raw <> "\"" else raw forM_ cueCatalog $ \x -> do tell "CATALOG " tellText (unMcn x) eol forM_ cueCdTextFile $ \x -> do tell "CDTEXTFILE " tellText (T.pack x) eol forM_ cuePerformer $ \x -> do tell "PERFORMER " tellText (unCueText x) eol forM_ cueTitle $ \x -> do tell "TITLE " tellText (unCueText x) eol forM_ cueSongwriter $ \x -> do tell "SONGWRITER " tellText (unCueText x) eol forM_ cueFiles $ \CueFile {..} -> do tell "FILE " tellText (T.pack cueFileName) tell " " tellText (renderFileType cueFileType) eol forM_ cueFileTracks $ \CueTrack {..} -> do currentTrack <- get modify succ tell " TRACK " tellText (formatNat currentTrack) tell " " tellText (renderTrackType cueTrackType) eol let flags = catMaybes [ if cueTrackDigitalCopyPermitted then Just "DCP" else Nothing , if cueTrackFourChannelAudio then Just "4CH" else Nothing , if cueTrackPreemphasisEnabled then Just "PRE" else Nothing , if cueTrackSerialCopyManagement then Just "SCMS" else Nothing ] unless (null flags) $ do tell " FLAGS " (tell . BB.byteString) (B.intercalate " " flags) eol forM_ cueTrackIsrc $ \x -> do tell " ISRC " tellText (unIsrc x) eol forM_ cueTrackTitle $ \x -> do tell " TITLE " tellText (unCueText x) eol forM_ cueTrackPerformer $ \x -> do tell " PERFORMER " tellText (unCueText x) eol forM_ cueTrackSongwriter $ \x -> do tell " SONGWRITER " tellText (unCueText x) eol forM_ cueTrackPregap $ \x -> do tell " PREGAP " tellText (showMmSsFf x) eol forM_ cueTrackPregapIndex $ \x -> do tell " INDEX 00 " tellText (showMmSsFf x) eol forM_ (NE.zip (NE.fromList [1..]) cueTrackIndices) $ \(n, index) -> do tell " INDEX " tellText (formatNat n) tell " " tellText (showMmSsFf index) eol forM_ cueTrackPostgap $ \x -> do tell " POSTGAP " tellText (showMmSsFf x) eol ---------------------------------------------------------------------------- -- Helpers -- | Format a 'Natural' padding it with zeros to 2 digits. formatNat :: Natural -> Text formatNat = T.pack . printf "%02d" -- | Render a 'CueFileType' as per the CUE specs. renderFileType :: CueFileType -> Text renderFileType Binary = "BINARY" renderFileType Motorola = "MOTOROLA" renderFileType Aiff = "AIFF" renderFileType Wave = "WAVE" renderFileType MP3 = "MP3" -- | Render a 'CueTrackType' as per the CUE specs. renderTrackType :: CueTrackType -> Text renderTrackType CueTrackAudio = "AUDIO" renderTrackType CueTrackCdg = "CDG" renderTrackType CueTrackMode1_2048 = "MODE1/2048" renderTrackType CueTrackMode1_2352 = "MODE1/2352" renderTrackType CueTrackMode2_2336 = "MODE2/2336" renderTrackType CueTrackMode2_2352 = "MODE2/2352" renderTrackType CueTrackCdi2336 = "CDI/2336" renderTrackType CueTrackCdi2352 = "CDI/2352"