module Data.Music.Lilypond (
Music(..),
Note(..),
Clef(..),
Mode(..),
Value,
toValue,
toLiteralValue,
PostEvent(..),
ChordPostEvent(..),
Articulation(..),
Markup(..),
HasMarkup(..),
Direction(..),
OctaveCheck(..),
BreathingSign(..),
Duration(..),
Pitch(..),
PitchName(..),
Accidental(..),
Octaves(..),
rest,
note,
chord,
chordHarm,
chordWithPost,
sequential,
simultaneous,
addPost,
addText,
addMarkup,
addDynamics,
addArticulation,
addText',
addMarkup',
addDynamics',
addArticulation',
beginTie,
beginGlissando,
beginBeam,
endBeam,
beginSlur,
endSlur,
beginPhraseSlur,
endPhraseSlur,
beginCresc,
endCresc,
beginDim,
endDim,
addAccent,
addMarcato,
addStaccatissimo,
addEspressivo,
addStaccato,
addTenuto,
addPortato,
addUpbow,
addDownbow,
addFlageolet,
addThumb,
addLeftHeel,
addRightHeel,
addLeftToe,
addRightToe,
addOpen,
addStopped,
addTurn,
addReverseTurn,
addTrill,
addPrall,
addMordent,
addPrallPrall,
addPrallMordent,
addUpPrall,
addDownPrall,
addUpMordent,
addDownMordent,
addPrallDown,
addPrallUp,
addLinePrall,
addSignumCongruentiae,
addShortFermata,
addFermata,
addLongFermata,
addVeryLongFermata,
addSegno,
addCoda,
addVarCoda,
foldMusic,
removeSingleChords,
)
where
import Control.Arrow ((<<<), (***), first, second)
import Data.Ratio
import Data.String
import Data.Default
import Data.Semigroup
import Data.VectorSpace
import Text.Pretty hiding (Mode)
import Music.Pitch.Literal
import Data.Music.Lilypond.Pitch
import Data.Music.Lilypond.Dynamics
import Data.Music.Lilypond.Value
data Music
= Rest (Maybe Duration) [PostEvent]
| Note Note (Maybe Duration) [PostEvent]
| Chord [(Note, [ChordPostEvent])] (Maybe Duration) [PostEvent]
| Sequential [Music]
| Simultaneous Bool [Music]
| Repeat Bool Int Music (Maybe (Music, Music))
| Tremolo Int Music
| Times Rational Music
| Transpose Pitch Pitch Music
| Relative Pitch Music
| Clef Clef
| Key Pitch Mode
| Time Integer Integer
| Breathe (Maybe BreathingSign)
| Tempo (Maybe String) (Maybe (Duration,Integer))
| New String (Maybe String) Music
| Context String (Maybe String) Music
| Set String Value
| Override String Value
| Revert String
deriving (Eq, Show)
foldMusic :: (Music -> Music) -> Music -> Music
foldMusic f = go
where
go (Sequential ms) = Sequential (fmap go ms)
go (Simultaneous b ms) = Simultaneous b (fmap go ms)
go (Repeat b i m qmm) = Repeat b i m (fmap (go *** go) qmm)
go (Tremolo n m) = Tremolo n (go m)
go (Times r m) = Times r (go m)
go (Transpose p p2 m) = Transpose p p2 (go m)
go (Relative p m) = Relative p (go m)
go (New s v m) = New s v (go m)
go (Context s v m) = Context s v (go m)
go x = f x
foldMusic' :: (Music -> Music)
-> (Music -> Music)
-> (Music -> Music)
-> Music -> Music
foldMusic' f g h = go
where
go m@(Rest _ _) = f m
go m@(Note _ _ _) = f m
go m@(Chord _ _ _) = f m
go m@(Clef _) = g m
go m@(Key _ _) = g m
go m@(Time _ _) = g m
go m@(Breathe _) = g m
go m@(Tempo _ _) = g m
go m@(Set _ _) = g m
go m@(Override _ _) = g m
go m@(Revert _) = g m
go (Sequential ms) = Sequential (fmap h ms)
go (Simultaneous b ms) = Simultaneous b (fmap h ms)
go (Repeat b i m qmm) = Repeat b i m (fmap (h *** h) qmm)
go (Tremolo n m) = Tremolo n (h m)
go (Times r m) = Times r (h m)
go (Transpose p p2 m) = Transpose p p2 (h m)
go (Relative p m) = Relative p (h m)
go (New s v m) = New s v (h m)
go (Context s v m) = Context s v (h m)
instance Pretty Music where
pretty (Rest d p) = "r" <> pretty d <> prettyList p
pretty (Note n d p) = pretty n <> pretty d <> prettyList p
pretty (Chord ns d p) = "<" <> nest 4 (sepByS "" $ fmap (uncurry (<>) <<< pretty *** pretty) ns) <> char '>'
<> pretty d <> prettyList p
pretty (Sequential xs) = "{" <=> nest 4 ((hsep . fmap pretty) xs) <=> "}"
pretty (Simultaneous False xs) = "<<" <//> nest 4 ((vcat . fmap pretty) xs) <//> ">>"
pretty (Simultaneous True xs) = "<<" <//> nest 4 ((sepByS " \\\\" . fmap pretty) xs) <//> ">>"
pretty (Repeat unfold times x alts) =
"\\repeat" <=> unf unfold <=> int times <=> pretty x <=> alt alts
where
unf p = if p then "unfold" else "volta"
alt Nothing = empty
alt (Just (x,y)) = "\\alternative" <> pretty x <> pretty y
pretty (Tremolo n x) =
"\\repeat tremolo" <+> pretty n <=> pretty x
pretty (Times n x) =
"\\times" <+> frac n <=> pretty x
where
frac n = pretty (numerator n) <> "/" <> pretty (denominator n)
pretty (Transpose from to x) =
"\\transpose" <+> pretty from <=> pretty to <=> pretty x
pretty (Relative p x) =
"\\relative" <=> pretty p <=> pretty x
pretty (Clef c) = "\\clef" <+> pretty c
pretty (Key p m) = "\\key" <+> pretty p <+> pretty m
pretty (Time m n) = "\\time" <+> (pretty m <> "/" <> pretty n)
pretty (Breathe Nothing) = "\\breathe"
pretty (Breathe a) = notImpl "Non-standard breath marks"
pretty (Tempo Nothing Nothing) = mempty
pretty (Tempo (Just t) Nothing) = "\\time" <+> pretty t
pretty (Tempo Nothing (Just (d,bpm))) = "\\time" <+> pretty d <+> "=" <+> pretty bpm
pretty (Tempo (Just t) (Just (d,bpm))) = "\\time" <+> pretty t <+> pretty d <+> "=" <+> pretty bpm
pretty (New typ name x) =
"\\new" <+> string typ <+> pretty name <+> pretty x
pretty (Context typ name x) =
"\\context" <+> string typ <+> pretty name <+> pretty x
pretty (Set name val) =
"\\set" <+> string name <+> "=" <+> pretty val
pretty (Override name val) =
"\\override" <+> string name <+> "=" <+> pretty val
pretty (Revert name) =
"\\revert" <+> string name
prettyList = hsep . fmap pretty
instance IsPitch Music where
fromPitch = (\p -> Note p (Just (1/4)) []) . fromPitch
instance AdditiveGroup Music where
zeroV = Rest (Just $ 1/4) []
a ^+^ b = Sequential [a,b]
negateV = error "No Data.Music.Lilypond.Music.negateV"
instance VectorSpace Music where
type Scalar Music = Duration
a *^ (Rest (Just d) p) = Rest (Just $ a*d) p
a *^ (Note n (Just d) p) = Note n (Just $ a*d) p
a *^ (Chord ns (Just d) p) = Chord ns (Just $ a*d) p
a *^ x = x
data Note
= NotePitch Pitch (Maybe OctaveCheck)
| DrumNotePitch (Maybe Duration)
deriving (Eq, Show)
instance Pretty Note where
pretty (NotePitch p Nothing) = pretty p
pretty (NotePitch p _) = notImpl "Non-standard pitch"
pretty (DrumNotePitch _) = notImpl "Non-standard pitch"
prettyList = hsep . fmap pretty
instance IsPitch Note where
fromPitch = (\p -> (NotePitch p Nothing)) . fromPitch
data Clef
= Treble
| Alto
| Tenor
| Bass
| French
| Soprano
| MezzoSoprano
| Baritone
| VarBaritone
| SubBass
| Percussion
| Tab
deriving (Eq, Show)
instance Pretty Clef where
pretty Treble = "treble"
pretty Alto = "alto"
pretty Tenor = "tenor"
pretty Bass = "bass"
pretty French = "french"
pretty Soprano = "soprano"
pretty MezzoSoprano = "mezzosoprano"
pretty Baritone = "baritone"
pretty VarBaritone = "varbaritone"
pretty SubBass = "subbass"
pretty Percussion = "percussion"
pretty Tab = "tab"
data BreathingSign
= RightVarComma
| StraightCaesura
| CurvedCaesura
deriving (Eq, Show)
data ChordPostEvent
= Harmonic
deriving (Eq, Show)
instance Pretty ChordPostEvent where
pretty Harmonic = "\\harmonic"
data PostEvent
= Articulation Direction Articulation
| Dynamics Direction Dynamics
| Tie
| Glissando
| BeginBeam
| EndBeam
| BeginSlur
| EndSlur
| BeginPhraseSlur
| EndPhraseSlur
| BeginCresc
| BeginDim
| EndCrescDim
| Text Direction String
| Markup Direction Markup
deriving (Eq, Show)
instance Pretty PostEvent where
pretty (Articulation d a) = pretty d <> pretty a
pretty (Dynamics d a) = pretty d <> pretty a
pretty Tie = "~"
pretty Glissando = "\\glissando"
pretty BeginBeam = "["
pretty EndBeam = "]"
pretty BeginSlur = "("
pretty EndSlur = ")"
pretty BeginPhraseSlur = "\\("
pretty EndPhraseSlur = "\\)"
pretty BeginCresc = "\\<"
pretty BeginDim = "\\>"
pretty EndCrescDim = "\\!"
pretty (Text d s) = pretty d <> (string . show) s
pretty (Markup d m) = pretty d <> ("\\markup" <+> pretty m)
prettyList = hcat . fmap pretty
data Markup
= MarkupText String
| MarkupList [Markup]
| Bold Markup
| Box Markup
| Caps Markup
| DynamicsFont Markup
| FingeringFont Markup
| Fontsize Double Markup
| Huge Markup
| Italic Markup
| Large Markup
| Larger Markup
| Magnify Markup
| Medium Markup
| Roman Markup
| Sans Markup
| Sub Markup
| Super Markup
| TextFont Markup
| Tiny Markup
| TypewriterFont Markup
| Upright Markup
deriving (Eq, Show)
class HasMarkup a where
markup :: a -> Markup
instance HasMarkup Markup where
markup = id
instance HasMarkup a => HasMarkup [a] where
markup = MarkupList . fmap markup
instance IsString Markup where
fromString = MarkupText
instance Pretty Markup where
pretty (MarkupText s) = (string . show) s
pretty (MarkupList as) = "{" <+> hsep (fmap pretty as) <+> "}"
pretty (Bold a) = "\\bold" <+> pretty a
pretty (Box a) = "\\box" <+> pretty a
pretty (Caps a) = "\\caps" <+> pretty a
pretty (DynamicsFont a) = "\\dynamics" <+> pretty a
pretty (FingeringFont a) = "\\fingering" <+> pretty a
pretty (Fontsize n a) = "\\fontsize" <+> ("#" <> pretty n) <+> pretty a
pretty (Huge a) = "\\huge" <+> pretty a
pretty (Italic a) = "\\italic" <+> pretty a
pretty (Large a) = "\\large" <+> pretty a
pretty (Larger a) = "\\larger" <+> pretty a
pretty (Magnify a) = "\\magnify" <+> pretty a
pretty (Medium a) = "\\medium" <+> pretty a
pretty (Roman a) = "\\roman" <+> pretty a
pretty (Sans a) = "\\sans" <+> pretty a
pretty (Sub a) = "\\sub" <+> pretty a
pretty (Super a) = "\\super" <+> pretty a
pretty (TextFont a) = "\\text" <+> pretty a
pretty (Tiny a) = "\\tiny" <+> pretty a
pretty (TypewriterFont a) = "\\typewriter" <+> pretty a
pretty (Upright a) = "\\upright" <+> pretty a
data Articulation
= Accent
| Marcato
| Staccatissimo
| Espressivo
| Staccato
| Tenuto
| Portato
| Upbow
| Downbow
| Flageolet
| Thumb
| LeftHeel
| RightHeel
| LeftToe
| RightToe
| Open
| Stopped
| Turn
| ReverseTurn
| Trill
| Prall
| Mordent
| PrallPrall
| PrallMordent
| UpPrall
| DownPrall
| UpMordent
| DownMordent
| PrallDown
| PrallUp
| LinePrall
| SignumCongruentiae
| ShortFermata
| Fermata
| LongFermata
| VeryLongFermata
| Segno
| Coda
| VarCoda
deriving (Eq, Show)
instance Pretty Articulation where
pretty Accent = ">"
pretty Marcato = "^"
pretty Staccatissimo = "!"
pretty Espressivo = "\\espressivo"
pretty Staccato = "."
pretty Tenuto = "-"
pretty Portato = "_"
pretty Upbow = "\\upbow"
pretty Downbow = "\\downbow"
pretty Flageolet = "\\flageolet"
pretty Thumb = "\\thumb"
pretty LeftHeel = "\\leftheel"
pretty RightHeel = "\\rightheel"
pretty LeftToe = "\\lefttoe"
pretty RightToe = "\\righttoe"
pretty Open = "\\open"
pretty Stopped = "+"
pretty Turn = "\\turn"
pretty ReverseTurn = "\\reverseturn"
pretty Trill = "\\trill"
pretty Prall = "\\prall"
pretty Mordent = "\\mordent"
pretty PrallPrall = "\\prallprall"
pretty PrallMordent = "\\prallmordent"
pretty UpPrall = "\\upprall"
pretty DownPrall = "\\downprall"
pretty UpMordent = "\\upmordent"
pretty DownMordent = "\\downmordent"
pretty PrallDown = "\\pralldown"
pretty PrallUp = "\\prallup"
pretty LinePrall = "\\lineprall"
pretty SignumCongruentiae = "\\signumCongruentiae"
pretty ShortFermata = "\\shortfermata"
pretty Fermata = "\\fermata"
pretty LongFermata = "\\longfermata"
pretty VeryLongFermata = "\\verylongfermata"
pretty Segno = "\\segno"
pretty Coda = "\\coda"
pretty VarCoda = "\\varcoda"
prettyList = hcat . fmap pretty
data Direction
= Above
| Default
| Below
deriving (Eq, Ord, Show)
instance Default Direction where
def = Default
instance Pretty Direction where
pretty Above = "^"
pretty Default = "-"
pretty Below = "_"
newtype Duration = Duration { getDuration :: Rational }
deriving instance Eq Duration
deriving instance Ord Duration
deriving instance Num Duration
deriving instance Enum Duration
deriving instance Fractional Duration
deriving instance Real Duration
deriving instance RealFrac Duration
deriving instance Show Duration
instance Pretty Duration where
pretty a = string $ pnv (toRational nv) ++ pds ds
where
pnv 4 = "\\longa"
pnv 2 = "\\breve"
pnv n = show (denominator n)
pds n = concat $ replicate n "."
(nv, ds) = separateDots a
rest :: Music
rest = Rest (Just $ 1/4) []
note :: Note -> Music
note n = Note n (Just $ 1/4) []
chord :: [Note] -> Music
chord ns = Chord (fmap (\x -> (x,[])) ns) (Just $ 1/4) []
chordHarm :: [(Note, Bool)] -> Music
chordHarm = chordWithPost . fmap (second $ \x -> if x then [Harmonic] else [])
chordWithPost :: [(Note, [ChordPostEvent])] -> Music
chordWithPost ns = Chord ns (Just $ 1/4) []
sequential :: Music -> Music -> Music
Sequential as `sequential` Sequential bs = Sequential (as <> bs)
Sequential as `sequential` b = Sequential (as <> [b])
a `sequential` Sequential bs = Sequential ([a] <> bs)
a `sequential` b = Sequential ([a,b])
simultaneous :: Music -> Music -> Music
Simultaneous s as `simultaneous` Simultaneous t bs = Simultaneous True (as <> bs)
Simultaneous s as `simultaneous` b = Simultaneous s (as <> [b])
a `simultaneous` Simultaneous t bs = Simultaneous t ([a] <> bs)
a `simultaneous` b = Simultaneous True ([a,b])
addPost :: PostEvent -> Music -> Music
addPost a = foldMusic' (addPost' a) id (addPost a)
where
addPost' a (Rest d es) = Rest d (es ++ [a])
addPost' a (Note n d es) = Note n d (es ++ [a])
addPost' a (Chord ns d es) = Chord ns d (es ++ [a])
addText :: String -> Music -> Music
addText s = addPost (Text def s)
addText' :: Direction -> String -> Music -> Music
addText' d s = addPost (Text d s)
addMarkup :: HasMarkup a => a -> Music -> Music
addMarkup s = addPost (Markup def (markup s))
addMarkup' :: HasMarkup a => Direction -> a -> Music -> Music
addMarkup' d s = addPost (Markup d (markup s))
addArticulation :: Articulation -> Music -> Music
addArticulation a = addPost (Articulation def a)
addArticulation' :: Direction -> Articulation -> Music -> Music
addArticulation' d a = addPost (Articulation d a)
addDynamics :: Dynamics -> Music -> Music
addDynamics a = addPost (Dynamics def a)
addDynamics' :: Direction -> Dynamics -> Music -> Music
addDynamics' d a = addPost (Dynamics d a)
beginTie :: Music -> Music
beginTie = addPost Tie
beginGlissando :: Music -> Music
beginGlissando = addPost Glissando
beginBeam :: Music -> Music
beginBeam = addPost BeginBeam
endBeam :: Music -> Music
endBeam = addPost EndBeam
beginSlur :: Music -> Music
beginSlur = addPost BeginSlur
endSlur :: Music -> Music
endSlur = addPost EndSlur
beginPhraseSlur :: Music -> Music
beginPhraseSlur = addPost BeginPhraseSlur
endPhraseSlur :: Music -> Music
endPhraseSlur = addPost EndPhraseSlur
beginCresc :: Music -> Music
beginCresc = addPost BeginCresc
endCresc :: Music -> Music
endCresc = addPost EndCrescDim
beginDim :: Music -> Music
beginDim = addPost BeginDim
endDim :: Music -> Music
endDim = addPost EndCrescDim
addAccent :: Music -> Music
addAccent = addArticulation Accent
addMarcato :: Music -> Music
addMarcato = addArticulation Marcato
addStaccatissimo :: Music -> Music
addStaccatissimo = addArticulation Staccatissimo
addEspressivo :: Music -> Music
addEspressivo = addArticulation Espressivo
addStaccato :: Music -> Music
addStaccato = addArticulation Staccato
addTenuto :: Music -> Music
addTenuto = addArticulation Tenuto
addPortato :: Music -> Music
addPortato = addArticulation Portato
addUpbow :: Music -> Music
addUpbow = addArticulation Upbow
addDownbow :: Music -> Music
addDownbow = addArticulation Downbow
addFlageolet :: Music -> Music
addFlageolet = addArticulation Flageolet
addThumb :: Music -> Music
addThumb = addArticulation Thumb
addLeftHeel :: Music -> Music
addLeftHeel = addArticulation LeftHeel
addRightHeel :: Music -> Music
addRightHeel = addArticulation RightHeel
addLeftToe :: Music -> Music
addLeftToe = addArticulation LeftToe
addRightToe :: Music -> Music
addRightToe = addArticulation RightToe
addOpen :: Music -> Music
addOpen = addArticulation Open
addStopped :: Music -> Music
addStopped = addArticulation Stopped
addTurn :: Music -> Music
addTurn = addArticulation Turn
addReverseTurn :: Music -> Music
addReverseTurn = addArticulation ReverseTurn
addTrill :: Music -> Music
addTrill = addArticulation Trill
addPrall :: Music -> Music
addPrall = addArticulation Prall
addMordent :: Music -> Music
addMordent = addArticulation Mordent
addPrallPrall :: Music -> Music
addPrallPrall = addArticulation PrallPrall
addPrallMordent :: Music -> Music
addPrallMordent = addArticulation PrallMordent
addUpPrall :: Music -> Music
addUpPrall = addArticulation UpPrall
addDownPrall :: Music -> Music
addDownPrall = addArticulation DownPrall
addUpMordent :: Music -> Music
addUpMordent = addArticulation UpMordent
addDownMordent :: Music -> Music
addDownMordent = addArticulation DownMordent
addPrallDown :: Music -> Music
addPrallDown = addArticulation PrallDown
addPrallUp :: Music -> Music
addPrallUp = addArticulation PrallUp
addLinePrall :: Music -> Music
addLinePrall = addArticulation LinePrall
addSignumCongruentiae :: Music -> Music
addSignumCongruentiae = addArticulation SignumCongruentiae
addShortFermata :: Music -> Music
addShortFermata = addArticulation ShortFermata
addFermata :: Music -> Music
addFermata = addArticulation Fermata
addLongFermata :: Music -> Music
addLongFermata = addArticulation LongFermata
addVeryLongFermata :: Music -> Music
addVeryLongFermata = addArticulation VeryLongFermata
addSegno :: Music -> Music
addSegno = addArticulation Segno
addCoda :: Music -> Music
addCoda = addArticulation Coda
addVarCoda :: Music -> Music
addVarCoda = addArticulation VarCoda
removeSingleChords :: Music -> Music
removeSingleChords = foldMusic go
where
go (Chord [(n,_)] d p) = Note n d p
go x = x
notImpl a = error $ "Not implemented: " ++ a
asPitch = id
asPitch :: Pitch -> Pitch
separateDots :: Duration -> (Duration, Int)
separateDots = separateDots' [2/3, 6/7, 14/15, 30/31, 62/63]
separateDots' :: [Duration] -> Duration -> (Duration, Int)
separateDots' [] nv = error "separateDots: Strange"
separateDots' (div:divs) nv
| isDivisibleBy 2 nv = (nv, 0)
| otherwise = (nv', dots' + 1)
where
(nv', dots') = separateDots' divs (nv*div)
logBaseR :: forall a . (RealFloat a, Floating a) => Rational -> Rational -> a
logBaseR k n
| isInfinite (fromRational n :: a) = logBaseR k (n/k) + 1
logBaseR k n
| isDenormalized (fromRational n :: a) = logBaseR k (n*k) 1
logBaseR k n = logBase (fromRational k) (fromRational n)
isDivisibleBy :: (Real a, Real b) => a -> b -> Bool
isDivisibleBy n = (equalTo 0.0) . snd . properFraction . logBaseR (toRational n) . toRational
equalTo :: Eq a => a -> a -> Bool
equalTo = (==)
infixl <=>
a <=> b = sep [a,b]
test = Simultaneous False [
New "StaffGroup" Nothing (Simultaneous False [
New "Staff" Nothing (Relative c' $ Sequential [
Set "Staff.instrumentName" (toValue "Violin I"),
(addDynamics FF c), d, e
]),
New "Staff" Nothing (Sequential [
Set "Staff.instrumentName" (toValue "Violin II"),
Clef Bass, c, g_, c])
])
]