\subsubsection{Duration} \seclabel{duration} \begin{haskelllisting} > module Haskore.Basic.Duration where > import qualified Medium.Temporal as TemporalMedium > import Data.Ratio((%)) > import qualified Haskore.General.Utility as Utility > import Haskore.General.Map (Map) > import qualified Haskore.General.Map as Map > import qualified Numeric.NonNegative.Wrapper as NonNeg \end{haskelllisting} \begin{haskelllisting} > type T = TemporalMedium.Dur > type Ratio = T > type Offset = Rational > infixl 7 %+ > (%+) :: Integer -> Integer -> T > (%+) x y = fromRatio (x%y) > fromRatio :: Rational -> T > fromRatio = NonNeg.fromNumberMsg "Duration.fromRatio" > toRatio :: T -> Rational > toRatio = NonNeg.toNumber > toNumber :: Fractional a => T -> a > toNumber = fromRational . NonNeg.toNumber > scale :: Ratio -> T -> T > scale = (*) > add :: Offset -> T -> T > add d = NonNeg.fromNumberMsg "Duration.add" . (d+) . toRatio \end{haskelllisting} \function{add} may have undefined result. \begin{haskelllisting} > divide :: T -> T -> Integer > divide r1 r2 = Utility.divide (toRatio r1) (toRatio r2) > divisible :: T -> T -> Bool > divisible r1 r2 = Utility.divisible (toRatio r1) (toRatio r2) > gcd :: T -> T -> T > gcd r1 r2 = fromRatio (Utility.gcdDur (toRatio r1) (toRatio r2)) \end{haskelllisting} \begin{haskelllisting} > dotted, doubleDotted :: T -> T > dotted = ((3%+2) *) > doubleDotted = ((7%+4) *) > > bn, wn, hn, qn, en, sn, tn, sfn :: T > dwn, dhn, dqn, den, dsn, dtn :: T > ddhn, ddqn, dden :: T > > bn = 2 -- brevis > wn = 1 -- whole note > hn = 1%+ 2 -- half note > qn = 1%+ 4 -- quarter note > en = 1%+ 8 -- eight note > sn = 1%+16 -- sixteenth note > tn = 1%+32 -- thirty-second note > sfn = 1%+64 -- sixty-fourth note > > dwn = dotted wn -- dotted whole note > dhn = dotted hn -- dotted half note > dqn = dotted qn -- dotted quarter note > den = dotted en -- dotted eighth note > dsn = dotted sn -- dotted sixteenth note > dtn = dotted tn -- dotted thirty-second note > > ddhn = doubleDotted hn -- double-dotted half note > ddqn = doubleDotted qn -- double-dotted quarter note > dden = doubleDotted en -- double-dotted eighth note \end{haskelllisting} \begin{haskelllisting} > nameDictionary :: Map T String > nameDictionary = > let names = "b" : "w" : "h" : "q" : "e" : "s" : "t" : "sf" : [] > durs = zip (iterate (/2) 2) names > ddurs = map (\(d,s) -> (dotted d, "d" ++s)) durs > dddurs = map (\(d,s) -> (doubleDotted d, "dd"++s)) durs > in Map.fromList $ > durs ++ > take 6 (drop 1 ddurs) ++ > take 3 (drop 2 dddurs) > {- | > Converts @1%4@ to @\"qn\"@ and so on. > -} > toString :: T -> String > toString dur = > maybe > ("(" ++ show dur ++ ")") > (++"n") > (Map.lookup nameDictionary dur) \end{haskelllisting} Check proper formatting. \begin{haskelllisting} > propToString :: Bool > propToString = > all (\(dur,name) -> toString dur == name) $ > (bn, "bn") : (wn, "wn") : (hn, "hn") : (qn, "qn") : > (en, "en") : (sn, "sn") : (tn, "tn") : (sfn, "sfn") : > (dwn, "dwn") : (dhn, "dhn") : (dqn, "dqn") : > (den, "den") : (dsn, "dsn") : (dtn, "dtn") : > (ddhn, "ddhn") : (ddqn, "ddqn") : (dden, "dden") : [] \end{haskelllisting}