% 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}