PageRenderTime 130ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/bala/Neume/src/Neume/Core/LilyPondOutput.hs

http://copperbox.googlecode.com/
Haskell | 324 lines | 141 code | 97 blank | 86 comment | 3 complexity | ca2a4b430a60fed8080540d112cc5da2 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1
  1. {-# LANGUAGE TypeFamilies #-}
  2. {-# LANGUAGE FlexibleInstances #-}
  3. {-# OPTIONS -Wall #-}
  4. --------------------------------------------------------------------------------
  5. -- |
  6. -- Module : Neume.Core.LilyPondOutput
  7. -- Copyright : (c) Stephen Tetley 2010
  8. -- License : BSD3
  9. --
  10. -- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>
  11. -- Stability : highly unstable
  12. -- Portability : GHC
  13. --
  14. -- Pretty print LilyPond
  15. --
  16. --------------------------------------------------------------------------------
  17. module Neume.Core.LilyPondOutput
  18. (
  19. LyOptionalDuration(..)
  20. , LyStdGlyph
  21. , LyStdNote
  22. , lyRelativeRewrite
  23. , lyAbsoluteRewrite
  24. , renderPhrase
  25. , renderGlyph
  26. , renderMarkupGlyph
  27. -- * rewriting
  28. , rewriteDurationOpt
  29. , rewritePitchAbs
  30. , rewritePitchAbs_treble
  31. , rewritePitchAbs_tab
  32. , rewritePitchRel
  33. ) where
  34. import Neume.Core.Duration
  35. import Neume.Core.LilyPondBasic
  36. import Neume.Core.Pitch
  37. import Neume.Core.SyntaxInterim
  38. import Neume.Core.SyntaxGlyph
  39. import Neume.Core.Utils
  40. import Neume.Core.Utils.OneList
  41. import Text.PrettyPrint.Leijen -- package: wl-print
  42. import qualified Data.Foldable as F
  43. -- Type changing operation ...
  44. --
  45. class LyOptionalDuration gly where
  46. type LyOptDuration gly :: *
  47. lyExtractDuration :: gly -> Duration
  48. toOptDuration :: Maybe Duration -> gly -> LyOptDuration gly
  49. -- Note graces are always written with their duration...
  50. --
  51. instance LyOptionalDuration (Glyph anno pch Duration) where
  52. type LyOptDuration (Glyph anno pch Duration) = Glyph anno pch (Maybe Duration)
  53. lyExtractDuration (GlyNote _ d _) = d
  54. lyExtractDuration (Rest d) = d
  55. lyExtractDuration (Spacer d) = d
  56. lyExtractDuration (Chord _ d _) = d
  57. lyExtractDuration (Graces _) = dZero
  58. toOptDuration od (GlyNote n _ t) = GlyNote n od t
  59. toOptDuration od (Rest _) = Rest od
  60. toOptDuration od (Spacer _) = Spacer od
  61. toOptDuration od (Chord xs _ t) = Chord xs od t
  62. toOptDuration _ (Graces xs) = Graces (fmap (fmap3c Just) xs)
  63. instance LyOptionalDuration (MarkupGlyph gly Duration) where
  64. type LyOptDuration (MarkupGlyph gly Duration) = MarkupGlyph gly (Maybe Duration)
  65. lyExtractDuration (MGlyph _ d) = d
  66. lyExtractDuration (Skip d) = d
  67. toOptDuration od (MGlyph gly _) = MGlyph gly od
  68. toOptDuration od (Skip _) = Skip od
  69. type LyStdGlyph anno = Glyph anno Pitch (Maybe Duration)
  70. type LyStdNote anno = Note anno Pitch
  71. -- This isn\'t right - relative pitch transform needs to return
  72. -- the final pitch so the trasformation can be \'stacked\' for
  73. -- successive phrases.
  74. -- Chaining doesn't work well if we use
  75. -- Ly_Relative_Rewrite_Config...
  76. lyRelativeRewrite :: Pitch
  77. -> CPhrase (Glyph anno Pitch Duration)
  78. -> (CPhrase (Glyph anno Pitch (Maybe Duration)), Pitch)
  79. lyRelativeRewrite pch = fmap2a rewriteDurationOpt . rewritePitchRel pch
  80. lyAbsoluteRewrite :: Int
  81. -> CPhrase (Glyph anno Pitch Duration)
  82. -> CPhrase (Glyph anno Pitch (Maybe Duration))
  83. lyAbsoluteRewrite i = rewriteDurationOpt . rewritePitchAbs i
  84. --------------------------------------------------------------------------------
  85. -- Render
  86. -- Note for lilypond percussion we might want either the long or
  87. -- short name printing, so renderPhrase isn't a good candidate
  88. -- for a Type Class.
  89. -- ignore annotations at the moment...
  90. -- renderPhrase show be parameterized with a function :: (gly -> Doc)...
  91. renderPhrase :: (gly -> Doc) -> CPhrase gly -> PhraseImage
  92. renderPhrase = oPhrase
  93. oPhrase :: (gly -> Doc) -> CPhrase gly -> PhraseImage
  94. oPhrase f (Phrase name bars) =
  95. PhraseImage name $ map (oBar f) bars
  96. oBar :: (gly -> Doc) -> CBar gly -> BarImage
  97. oBar f = hsep . oCExprList f
  98. oCExprList :: (gly -> Doc) -> [CExpr gly] -> [Doc]
  99. oCExprList f = map (oCExpr f)
  100. oCExpr :: (gly -> Doc) -> CExpr gly -> Doc
  101. oCExpr f (Atom e) = f e
  102. oCExpr f (N_Plet mp xs) = pletForm mp (oCExprList f xs)
  103. oCExpr f (Beamed notes) = beamForm $ oCExprList f notes
  104. -- annos gerally printed _after_ duration...
  105. renderGlyph :: (pch -> Doc) -> (anno -> DocS)
  106. -> Glyph anno pch (Maybe Duration)
  107. -> Doc
  108. renderGlyph = oGlyph
  109. -- this is a hack to get anno as a suffix - needs improving ...
  110. oGlyph :: (pch -> Doc) -> (anno -> DocS) -> GlyphRelDur anno pch -> Doc
  111. oGlyph f g (GlyNote n d t) = oNote f g d n <> optDoc t tie
  112. oGlyph _ _ (Rest d) = rest d
  113. oGlyph _ _ (Spacer d) = spacer d
  114. oGlyph f g (Chord ps d t) = chordForm (toListF (oNote f g Nothing) ps) d <> optDoc t tie
  115. oGlyph f g (Graces os) = graceForm $ oGraceNotes f g os
  116. oNote :: (pch -> Doc) -> (anno -> DocS) -> Maybe Duration -> Note anno pch -> Doc
  117. oNote f g od (Note a p) = g a (f p <> maybe empty duration od)
  118. oGraceNotes :: (pch -> Doc)
  119. -> (anno -> DocS)
  120. -> OneList (GraceNote anno pch (Maybe Duration))
  121. -> [Doc]
  122. oGraceNotes f g = map gf . F.toList where
  123. gf (GraceNote a p d) = g a (f p <> maybe empty duration d)
  124. renderMarkupGlyph :: (gly -> Maybe Duration -> Doc)
  125. -> MarkupGlyph gly (Maybe Duration)
  126. -> Doc
  127. renderMarkupGlyph f (MGlyph g d) = f g d
  128. renderMarkupGlyph _ (Skip d) = spacer d
  129. --------------------------------------------------------------------------------
  130. -- Rewrite Duration
  131. -- LilyPond has a shorthand notation thats a variation on
  132. -- run-length-encoding - successive equal durations are elided.
  133. --
  134. -- For (post-)composabilty the first note in a bar should be
  135. -- printed with a duration regardless of the duration of its
  136. -- predecessor.
  137. --
  138. -- Also it doesn't seem useful to 'compact' dotted durations.
  139. -- (explanation needed! [Currently, I've forgotten why...])
  140. --
  141. -- Note - seed each bar with the default duration.
  142. -- This makes scores clearer.
  143. --
  144. -- | A quarter note
  145. default_duration :: Duration
  146. default_duration = dQuarter
  147. -- This one is more complicated than expected...
  148. -- It has to look at the first 'note' of a bar - the first note
  149. -- may be the first note inside a Tuplet or beam group
  150. -- so it is not a standard map (nor a firstSpecial_st either).
  151. --
  152. -- Suggests bringing back shape/contents traversals... ?
  153. --
  154. rewriteDurationOpt :: (LyOptionalDuration gly, gly' ~ LyOptDuration gly)
  155. => CPhrase gly -> CPhrase gly'
  156. rewriteDurationOpt =
  157. stmapBarInitialGlyph doptGlyph1 doptGlyphN default_duration
  158. -- Never replace the duration of the first note in a bar.
  159. --
  160. doptGlyph1 :: (LyOptionalDuration gly, gly' ~ LyOptDuration gly)
  161. => Duration -> gly -> (gly',Duration)
  162. doptGlyph1 _ gly = (gly',d)
  163. where
  164. d = lyExtractDuration gly
  165. gly' = toOptDuration (Just d) gly
  166. -- Never replace the duration of the first note in a bar.
  167. --
  168. doptGlyphN :: (LyOptionalDuration gly, gly' ~ LyOptDuration gly)
  169. => Duration -> gly -> (gly',Duration)
  170. doptGlyphN old gly = (gly',d)
  171. where
  172. d = lyExtractDuration gly
  173. gly' = if (d==old && notDotted d)
  174. then toOptDuration Nothing gly
  175. else toOptDuration (Just d) gly
  176. --------------------------------------------------------------------------------
  177. -- Rewrite Pitch
  178. -- Absolute
  179. -- Middle C in Neume is C-octave 4.
  180. --
  181. -- Middle C in LilyPond is c' - in Mullein terms, after the
  182. -- absolute pitch transformation, this is C-octave 1, the
  183. -- octave designator has 3 subtracted to represent the number
  184. -- of apostrophes to print (a negative number represents the
  185. -- number of commas to print, after taking the @abs@ of the
  186. -- value).
  187. --
  188. -- HOWEVER, printing guitar tablature in absolute mode seems to
  189. -- take middle c as C (C-octave 0), so 4 has to be subtracted
  190. -- from the octave designator.
  191. --
  192. -- TODO - find out why this is the case.
  193. -- TODO - should these move to a type class / family like
  194. -- duration? Are there other pitched glyph types...
  195. rewritePitchAbs :: Int
  196. -> CPhrase (Glyph anno Pitch dur)
  197. -> CPhrase (Glyph anno Pitch dur)
  198. rewritePitchAbs i = mapCPhrase (abspGlyph i)
  199. rewritePitchAbs_treble :: CPhrase (Glyph anno Pitch dur)
  200. -> CPhrase (Glyph anno Pitch dur)
  201. rewritePitchAbs_treble = rewritePitchAbs (-3)
  202. rewritePitchAbs_tab :: CPhrase (Glyph anno Pitch dur)
  203. -> CPhrase (Glyph anno Pitch dur)
  204. rewritePitchAbs_tab = rewritePitchAbs (-4)
  205. abspGlyph :: Int -> Glyph anno Pitch dur -> Glyph anno Pitch dur
  206. abspGlyph i (GlyNote n d t) = GlyNote (abspNote i n) d t
  207. abspGlyph _ (Rest d) = Rest d
  208. abspGlyph _ (Spacer d) = Spacer d
  209. abspGlyph i (Chord os d t) = Chord (fmap (abspNote i) os) d t
  210. abspGlyph i (Graces os) = Graces $ fmap (abspGraceNote i) os
  211. abspNote :: Int -> Note anno Pitch -> Note anno Pitch
  212. abspNote i (Note a p) = Note a (displaceOctave i p)
  213. abspGraceNote :: Int -> GraceNote anno Pitch dur -> GraceNote anno Pitch dur
  214. abspGraceNote i (GraceNote a p d) = GraceNote a (displaceOctave i p) d
  215. --------------------------------------------------------------------------------
  216. -- Relative Pitch
  217. rewritePitchRel :: Pitch
  218. -> CPhrase (Glyph anno Pitch dur)
  219. -> (CPhrase (Glyph anno Pitch dur), Pitch)
  220. rewritePitchRel pch = stmapCPhrase relpGlyph pch
  221. relpGlyph :: Pitch -> Glyph anno Pitch dur -> (Glyph anno Pitch dur,Pitch)
  222. relpGlyph = stmap3b relpP
  223. -- | Need to return the \original\ pitch as the state, not the
  224. -- octave modified new value.
  225. --
  226. relpP :: Pitch -> Pitch -> (Pitch,Pitch)
  227. relpP prev p = let p' = setOctave (lyOctaveDist prev p) p in (p',p)