% from AutoTrack by Stefan Ratschan \begin{haskelllisting}

    > module Haskore.Interface.AutoTrack.ChordChart
    >           (T(Cons), bars, hasChord,
    >            length, concat) where

    > import qualified Haskore.Music        as Music
    > import qualified Haskore.Interface.AutoTrack.ChartBar as ChartBar
    > import qualified Haskore.Interface.AutoTrack.Transposeable as Transposeable
    > import qualified Haskore.Basic.Duration as Dur
    > import           Haskore.Basic.Duration (wn, (%+), )
    > import           Data.Char(isSpace)
    > import qualified Data.List as List

    > import Prelude hiding (length, concat)

\end{haskelllisting} Chord charts are lists of bars. They have the following input syntax: \begin{verbatim} chart = { (bar | '%') '|' } \end{verbatim} The character '\%' is a shortcut for the same bar as before. Comments can occur everywhere in the text. They start with "--" and continue till the end of the current line. \begin{haskelllisting}

    > data T = Cons {bars :: [ ChartBar.T ] } deriving Show

    > length :: Integral a => T -> a
    > length = fromIntegral . List.length . bars

    > instance Read T where
    >   readsPrec _ = Haskore.Interface.AutoTrack.ChordChart.read

    > concat :: T -> T -> T
    > concat (Cons x) (Cons y) = Cons (x++y)

    > read :: ReadS T
    > read s = read1 (ChartBar.Cons wn []) (filterComment s)

    > filterComment :: String -> String
    > filterComment ('-':'-':r) = filterComment (tail (snd (break (=='\n') r)))
    > filterComment (c:r) = (c:filterComment r)
    > filterComment "" = ""

    > read1 :: ChartBar.T -> ReadS T
    > read1 lb (c:r) | isSpace c =
    >     read1 lb (dropWhile isSpace r)
    > read1 lb ('%':r) =
    >     [ (Cons (lb:br), r2) |
    >         ('|':r1) <- [ dropWhile isSpace r ],
    >         (Cons br, r2) <- read1 lb r1 ]
    > read1 (ChartBar.Cons sig _) s@(_:_) =
    >     [ (Cons (b:br), r1) |
    >         (b, ('|':r)) <- ChartBar.readChordSymbol sig Nothing s,
    >         (Cons br, r1) <- read1 b r ]
    > read1 _ s = [ (Cons [], s) ]

\end{haskelllisting} Chord charts can be transposed. \begin{haskelllisting}

    > instance Transposeable.C T where
    >   transpose i (Cons c) = Cons (fmap (Transposeable.transpose i) c)

\end{haskelllisting} We can extract a Boolean list from a chord chart that tells whether there is a chord at a certain position (hc[i] is true iff d*i has a chord). \begin{haskelllisting}

    > hasChord :: T -> (Music.Dur, [ Bool ] )
    > hasChord c =
    >    let g = barGCD c
    >    in (g, hasChord1 g c)

    > hasChord1 :: Music.Dur -> T -> [ Bool ]
    > hasChord1 bDur (Cons c) = List.concat (map (hasChordBar bDur) c)

    > barUnit :: ChartBar.T -> Music.Dur -> Dur.Ratio
    > barUnit bar d = d * (1 %+ ChartBar.length bar)

    > hasChordBar :: Music.Dur -> ChartBar.T -> [ Bool ]
    > hasChordBar bDur bar@(ChartBar.Cons d chords) =
    >    let times =
    >           fromInteger
    >              (Dur.divide (barUnit bar d) bDur)
    >        createList = replicate times . maybe False (const True)
    >    in  concatMap createList chords

    > barGCD :: T -> Music.Dur
    > barGCD (Cons c) =
    >    let chordDur bar = barUnit bar (ChartBar.dur bar)
    >    in  foldr1 Dur.gcd (map chordDur c)

\end{haskelllisting}