Copyright | (c) 2022 Andrew A. Cashner |
---|---|
Stability | Experimental |
Safe Haskell | None |
Language | Haskell2010 |
This module reads (lectio, Latin, "I read") and process input text to be set to music using the ark.
Kircher's specification
Kircher expects the user to prepare a text by segmenting it into phrases according to the poetic meter and prosody. In his description the texts are Latin, but he also demonstrates how the machine could be used with Aramaic and other languages, ideally by Jesuit missionaries.
Implementation
XML input
In our implementation we also expect the user to mark the input text by
dividing the syllables with hyphens and marking the long syllables with accent
symbols (`
, placed before the relevant syllable), for example:
Lau-`da-te `Do-mi-num `om-nis `ter-rae. Al-le-`lu-ia. A-`men.
This implementation takes input in the form of an XML document, in which the
text is syllabified and accented as just demonstrated, and divided into one or
more sections. In the attributes for each <section>
element, the user sets
the values we need as input for the ark:
textMeter
- e.g.,
Prose
orAdonium
musicMeter
Duple
,TripleMinor
, orTripleMajor
style
Simple
(= Syntagma I) orFlorid
(= Syntagma II)tone
- e.g.,
Tone1
Within each section the text is divided into one or more line groups (<lg>
)
and lines (<l>
). (These elements are borrowed from TEI.)
TODO
In Prose meter, Kircher leaves it up to the user to divide the text into phrases. We are currently using a very simple algorithm to divide the text into phrase groups within the correct size range. It would be better to use a more sophisticated algorithm to parse the text into optimal groups.
Reading and parsing the input file
The main function is prepareInput
, which reads and parses the file and produces a list of LyricSection
s.
This module reads the input file, parses the XML tree to extract the text and
needed parameters for setting the text (within each section), and then
packages the text into its own data structures to pass on to the other parts
of the program (Cogito
for processing and Scribo
for writing output).
Capturing XML data
The text is first grouped into intermediate data structures that closely
reflect the XML structure. Each <section>
becomes an ArkTextSection
,
containing a nested list of strings (line groups and lines from XML) and an
ArkConfig
with the parameters from the XML section attributes. The list of
these is packaged into a single ArkInput
structure containing metadata for
the whole document (taken from the XML <head>
), and a list of
ArkTextSection
s.
Preparing for musical setting
The module then processes this data and converts it into a list of
LyricSection
s that the other modules will use. Below are the structures
that are passed on to other modules, from top down. Each structure contains
the element below it, plus information about it (length, number of syllables,
etc.). To get that information, these structures are created with methods that
calculate the data upfront.
LyricSection
- group of sentences (from
<section>
)
- also contains an
ArkConfig
with the text-setting parameters
LyricSentence
- group of phrases (from
<lg>
) LyricPhrase
- group of words (from
<l>
) Verbum
- individual word, broken into syllables
Synopsis
- hyphenChar :: Char
- accentChar :: Char
- data ArkMetadata = ArkMetadata {}
- data ArkInput = ArkInput {}
- data ArkTextSection = ArkTextSection {}
- xmlSearch :: String -> QName
- xmlNodeText :: String -> Element -> String
- cleanUpText :: [String] -> [String]
- strip :: String -> String
- readInput :: String -> ArkInput
- parseSection :: Element -> ArkTextSection
- type SylLen = PenultLength
- data Verbum = Verbum {
- verbumText :: String
- verbumSyl :: [String]
- sylCount :: Int
- penultLength :: SylLen
- data LyricPhrase = LyricPhrase {}
- type PhrasesInLyricSentence = Int
- type PhrasesInLyricSection = [PhrasesInLyricSentence]
- data LyricSentence = LyricSentence {}
- data LyricSection = LyricSection {}
- sectionPhraseLengths :: LyricSection -> PhrasesInLyricSection
- inputPhraseLengths :: [LyricSection] -> [PhrasesInLyricSection]
- newLyricSentence :: [LyricPhrase] -> LyricSentence
- newLyricPhrase :: [Verbum] -> LyricPhrase
- newVerbum :: String -> Verbum
- penultValue :: [String] -> SylLen
- penult :: [a] -> Maybe a
- rephrase :: Int -> LyricPhrase -> [LyricPhrase]
- prepareInput :: ArkInput -> [LyricSection]
Read input file
Global settings for input format
hyphenChar :: Char Source #
The character used to demarcate syllables (default '-'
)
accentChar :: Char Source #
The character used at the beginning of syllables to show long (or
accented) syllables (default '`'
)
Storing XML data
data ArkMetadata Source #
Header information
Instances
Show ArkMetadata Source # | |
Defined in Lectio showsPrec :: Int -> ArkMetadata -> ShowS # show :: ArkMetadata -> String # showList :: [ArkMetadata] -> ShowS # |
The input to the ark is an ArkConfig
element with tone, style, and
meter; and a list of strings, each of which will become a LyricSentence
data ArkTextSection Source #
A section of input text (from xml section element)
Instances
Show ArkTextSection Source # | |
Defined in Lectio showsPrec :: Int -> ArkTextSection -> ShowS # show :: ArkTextSection -> String # showList :: [ArkTextSection] -> ShowS # |
Get the text from a node
cleanUpText :: [String] -> [String] Source #
For each string in list, break text into strings at newlines, strip leading and trailing whitespace, remove empty strings, remove newlines
readInput :: String -> ArkInput Source #
Read an XML string and return the data for input to the ark (ArkInput
)
parseSection :: Element -> ArkTextSection Source #
Parse an XML node tree into a section with configuration and parsed text.
Hierarchical text groupings by word, phrase, and sentence
Verbum
: Single words and syllables
Our data type for a word includes the original text of the word, that text chunked into syllables, the count of those syllables, and a marker of whether the penultimate syllable is short or long.
Verbum | |
|
LyricPhrase
: Multiple words
data LyricPhrase Source #
A LyricPhrase
is a group of Verbum
items (words): it contains the list of
words, the total count of syllables in the phrase, and a marker for the
phrase's penultimate syllable length.
LyricPhrase | |
|
Instances
Eq LyricPhrase Source # | |
Defined in Lectio (==) :: LyricPhrase -> LyricPhrase -> Bool # (/=) :: LyricPhrase -> LyricPhrase -> Bool # | |
Ord LyricPhrase Source # | |
Defined in Lectio compare :: LyricPhrase -> LyricPhrase -> Ordering # (<) :: LyricPhrase -> LyricPhrase -> Bool # (<=) :: LyricPhrase -> LyricPhrase -> Bool # (>) :: LyricPhrase -> LyricPhrase -> Bool # (>=) :: LyricPhrase -> LyricPhrase -> Bool # max :: LyricPhrase -> LyricPhrase -> LyricPhrase # min :: LyricPhrase -> LyricPhrase -> LyricPhrase # | |
Show LyricPhrase Source # | |
Defined in Lectio showsPrec :: Int -> LyricPhrase -> ShowS # show :: LyricPhrase -> String # showList :: [LyricPhrase] -> ShowS # |
LyricSentence
: Multiple phrases
type PhrasesInLyricSentence = Int Source #
Each sentence includes the number of phrases therein
type PhrasesInLyricSection = [PhrasesInLyricSentence] Source #
A list of totals of phrases in a section
data LyricSentence Source #
A LyricSentence
is just a list of LyricPhrase
items.
LyricSentence | |
|
Instances
Eq LyricSentence Source # | |
Defined in Lectio (==) :: LyricSentence -> LyricSentence -> Bool # (/=) :: LyricSentence -> LyricSentence -> Bool # | |
Ord LyricSentence Source # | |
Defined in Lectio compare :: LyricSentence -> LyricSentence -> Ordering # (<) :: LyricSentence -> LyricSentence -> Bool # (<=) :: LyricSentence -> LyricSentence -> Bool # (>) :: LyricSentence -> LyricSentence -> Bool # (>=) :: LyricSentence -> LyricSentence -> Bool # max :: LyricSentence -> LyricSentence -> LyricSentence # min :: LyricSentence -> LyricSentence -> LyricSentence # | |
Show LyricSentence Source # | |
Defined in Lectio showsPrec :: Int -> LyricSentence -> ShowS # show :: LyricSentence -> String # showList :: [LyricSentence] -> ShowS # |
LyricSection
: Multiple sentences with parameters for text-setting
data LyricSection Source #
A LyricSection
includes a list of LyricSentence
s and an ArkConfig
.
Including an ArkConfig
structure makes it possible to structure the input
text and program the ark to change meters or tones for different sections.
Instances
Eq LyricSection Source # | |
Defined in Lectio (==) :: LyricSection -> LyricSection -> Bool # (/=) :: LyricSection -> LyricSection -> Bool # | |
Ord LyricSection Source # | |
Defined in Lectio compare :: LyricSection -> LyricSection -> Ordering # (<) :: LyricSection -> LyricSection -> Bool # (<=) :: LyricSection -> LyricSection -> Bool # (>) :: LyricSection -> LyricSection -> Bool # (>=) :: LyricSection -> LyricSection -> Bool # max :: LyricSection -> LyricSection -> LyricSection # min :: LyricSection -> LyricSection -> LyricSection # | |
Show LyricSection Source # | |
Defined in Lectio showsPrec :: Int -> LyricSection -> ShowS # show :: LyricSection -> String # showList :: [LyricSection] -> ShowS # |
Get phrase lengths for prepared text
sectionPhraseLengths :: LyricSection -> PhrasesInLyricSection Source #
Get the number of phrases per sentence for a whole section.
inputPhraseLengths :: [LyricSection] -> [PhrasesInLyricSection] Source #
Get the phrase lengths for the whole input structure
Methods to read and store textual data into the above structures
newLyricSentence :: [LyricPhrase] -> LyricSentence Source #
Make a LyricSentence
from a list of LyricPhrase
s.
newLyricPhrase :: [Verbum] -> LyricPhrase Source #
Take a simple list of Verbum
items and make a LyricPhrase
structure from
it: the original list is stored as phraseText
, and the phraseSylCount
and phrasePenultLength
are calculated from that list.
The phraseSylCount
is the sum of all the sylCount
s of the words in the
list. The phrasePenultLength
is the penultLength
of the last list item.
newVerbum :: String -> Verbum Source #
Take a String
and create a Verbum
structure:
- strip the text of diacritics by removing
hyphenChar
andaccentChar
characters - extract syllables by stripping accents and splitting at hyphens
- get syllable count from list created in previous step
- get penultimate syllable length from list of syllables including
accents, using
penultValue
Helper methods for parsing
penultValue :: [String] -> SylLen Source #
Determine the length of the next-to-last in a list of strings.
If the list length is 1 or shorter, or if there is no accentChar
at the
beginning of the penultimate syllable (found using penult
), then the
result is Short
; otherwise Long
.
Grouping prose
:: Int | maximum syllable count per group |
-> LyricPhrase | text already parsed into a |
-> [LyricPhrase] | old phrase broken into list of phrases |
Regroup a phrase int groups of words with total syllable count in each group not to exceed a given maximum.
TODO: Replace with more sophisticated algorithm: - what to do if word is longer than maxSyllables? (break it into parts?) - optimize this for best grouping, not just most convenient in-order
Read the whole text
prepareInput :: ArkInput -> [LyricSection] Source #
Prepare the entire input structure