-------------------------------------------------------------------------------- -- | -- Module : Billboard.BillboardParser -- Copyright : (c) 2012--2013 Utrecht University -- License : LGPL-3 -- -- Maintainer : W. Bas de Haas -- Stability : experimental -- Portability : non-portable -- -- Summary: Modelling musical time (in a minimalistic way) with beats, bars -- and time signatures. -------------------------------------------------------------------------------- module Billboard.BeatBar ( TimeSig (..) , BeatWeight (..) , beatWeight , tatumsPerBar , chordsPerDot ) where -------------------------------------------------------------------------------- -- Modelling Beat and Bar structures (for chord sequences) -------------------------------------------------------------------------------- -- TODO explain Change: perhaps this should be separated -- | Barlines can have different weights. Among other applications, this is used -- in the printing of chord sequences. data BeatWeight = UnAligned | Beat | Change | Bar | Bar4 | Bar8 | Bar16 | LineStart deriving (Eq, Ord, Enum) instance Show BeatWeight where show UnAligned = "" show Change = "" show Beat = "." show Bar = "|" show Bar4 = "|\n|" show Bar8 = "|\n|" show Bar16 = "|\n|" show LineStart = "|\n|" -- | Model a time signature as a fraction newtype TimeSig = TimeSig {timeSig :: (Int, Int)} deriving (Eq) instance Show TimeSig where show (TimeSig (num, denom)) = show num ++ '/' : show denom -- | Defines the "metrical weight of a bar". A regular beat has strength 0, -- a bar has strength 1, a bar after 4 bars 2, a bar after 8 bars 3, and a bar -- after 16 bars 4. beatWeight :: TimeSig -> Int -> BeatWeight beatWeight ts pos = let tatum = tatumsPerBar ts in case (mod pos tatum, mod pos (4*tatum), mod pos (8*tatum), mod pos (16*tatum)) of (0,0,0,0) -> Bar16 -- a bar @ sixteen measures, weight 4 (0,0,0,_) -> Bar8 -- a bar @ eight measures (0,0,_,_) -> Bar4 -- a bar @ four measures (0,_,_,_) -> Bar -- a bar position _ -> Beat -- a regular beat position, weight 0 -- | Returns the number of 'tatums' in a bar which is different for time -- signatures. For example: -- -- >>> tatumsPerBar (TimeSig (3 ,4)) -- 6 -- -- >>> tatumsPerBar (TimeSig (6 ,8)) -- 6 -- -- >>> tatumsPerBar (TimeSig (12,8)) -- 12 -- -- N.B. This function is not strictly correct music-theoretically, but -- it reflects how Billboard annotators used time signatures. tatumsPerBar :: TimeSig -> Int tatumsPerBar (TimeSig (beats , 4)) = 2 * beats tatumsPerBar (TimeSig (beats , 8)) = beats tatumsPerBar ts = irregularMeterError ts "illegal denominator" -- | Returns the number of 'BBChord' that are inserted for one ".", based on -- a 'TimeSig'nature. chordsPerDot :: TimeSig -> Int chordsPerDot (TimeSig (_ , 4)) = 2 chordsPerDot (TimeSig (_ , 8)) = 3 chordsPerDot ts = irregularMeterError ts "illegal denominator" irregularMeterError :: TimeSig -> String -> a irregularMeterError ts msg = error ("Irregular meter: " ++ show ts ++ ' ' : msg)