forked from DCMLab/protovoices-haskell
-
Notifications
You must be signed in to change notification settings - Fork 0
/
MainISMIR.hs
135 lines (120 loc) · 4.21 KB
/
MainISMIR.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
{-# LANGUAGE QualifiedDo #-}
module Main where
import ChartParser
import Common
import Display
import PVGrammar hiding
( slicesFromFile
, slicesToPath
)
import PVGrammar.Generate
import PVGrammar.Parse
import Musicology.Core
import Musicology.Core.Slicing
import Musicology.MusicXML
import Musicology.Pitch.Spelled as MT
import Data.Maybe (mapMaybe)
import Data.Text.Lazy qualified as TL
import Data.Text.Lazy.IO qualified as TL
-- better do syntax
import Language.Haskell.DoNotation qualified as Do
-- | The example derivation shown in Figure 4, specified manually.
deriv321sus :: [PVLeftmost (Pitch MT.SIC)]
deriv321sus = buildDerivation $ Do.do
split $ mkSplit $ do
splitRegular Start Stop (c' nat) RootNote False False
splitRegular Start Stop (e' nat) RootNote False False
spread $ mkSpread $ do
spreadNote (c' nat) ToBoth True
spreadNote (e' nat) (ToLeft 1) False
addPassing (e' nat) (c' nat)
freeze FreezeOp
split $ mkSplit $ do
splitRegular (Inner $ c' nat) (Inner $ c' nat) (b' nat) FullNeighbor True False
splitPassing (e' nat) (c' nat) (d' nat) PassingMid True False
split $ mkSplit $ do
splitRegular
(Inner $ e' nat)
(Inner $ d' nat)
(d' nat)
LeftRepeatOfRight
False
True
splitRegular
(Inner $ c' nat)
(Inner $ b' nat)
(c' nat)
RightRepeatOfLeft
True
False
freeze FreezeOp
freeze FreezeOp
freeze FreezeOp
freeze FreezeOp
{- | The musical surface from Figure 4 as a sequence of slices and transitions.
Can be used as an input for parsing.
-}
path321sus =
Path [e' nat, c' nat] [(Inner $ c' nat, Inner $ c' nat)] $
Path [d' nat, c' nat] [(Inner $ d' nat, Inner $ d' nat)] $
Path [d' nat, b' nat] [] $
PathEnd [c' nat]
{- | The main function that produces the results used in the paper and demonstrates the parser:
* a diagram of the (manually specified) derivation of the suspension example
(similar to what is shown in Figure 4)
rendered to 321sus.{tex,pdf}
* the number of derivations of the suspension example (Figure 4)
* the number of derivations of the beginning of the Bach example (Figure 1)
* an abritrary derivation of the suspension examle generated by the parser
rendered to 321sus-parsed.{tex,pdf}
-}
main :: IO ()
main = do
plotDeriv "321sus.tex" deriv321sus
putStrLn "counting 321sus..."
count321sus <- parseSilent pvCountNoRepSplitRightBranchSplitFirst path321sus
count321sus' <- parseSilent pvCountUnrestricted path321sus
putStrLn "counting Bach..."
bachSlices <- slicesFromFile "testdata/allemande.musicxml"
bachCount <- parseSize pvCountNoRepSplitRightBranchSplitFirst $ slicesToPath $ take 9 bachSlices
bachCount' <- parseSize pvCountUnrestricted $ slicesToPath $ take 9 bachSlices
putStrLn "Results:"
putStrLn $ "number of derivations (321sus): " <> show count321sus
putStrLn $ "number of derivations (bach): " <> show bachCount
putStrLn $ "number of derivations (321sus, no restrictions): " <> show count321sus'
putStrLn $ "number of derivations (bach, no restrictions): " <> show bachCount'
putStrLn "derivation of 321sus:"
-- mapM_ print parse321
-- helper functions
-- ----------------
plotDeriv fn deriv = do
case replayDerivation derivationPlayerPV deriv of
(Left err) -> putStrLn err
(Right g) -> viewGraph fn g
slicesFromFile :: FilePath -> IO [[(SPitch, RightTied)]]
slicesFromFile file = do
txt <- TL.readFile file
case parseWithoutIds txt of
Nothing -> pure []
Just doc -> do
let (xmlNotes, _) = parseScore doc
notes = asNoteHeard <$> xmlNotes
slices = slicePiece tiedSlicer notes
pure $ mkSlice <$> filter (not . null) slices
where
mkSlice notes = mkNote <$> notes
mkNote (note, tie) = (pitch note, rightTie tie)
slicesToPath
:: (Interval i, Ord (ICOf i), Eq i)
=> [[(Pitch i, RightTied)]]
-> Path [Pitch (ICOf i)] [Edge (Pitch (ICOf i))]
slicesToPath = go
where
mkSlice = fmap (pc . fst)
mkEdges = mapMaybe mkEdge
where
mkEdge (p, Ends) = Nothing
mkEdge (p, Holds) = let p' = pc p in Just (Inner p', Inner p')
go [] = error "cannot construct path from empty list"
go [notes] = PathEnd (mkSlice notes)
go (notes : rest) = Path (mkSlice notes) (mkEdges notes) $ go rest