arca- A digital implementation of Athanasius Kircher's device for automatic music composition, the Arca musarithmica of 1650

Copyright(c) 2022 Andrew A. Cashner
Safe HaskellNone




This module is our implementation of Kircher's palimpsest phonotacticum, his system for writing out the music created using the ark. Certain elements that Kircher used notation to calculate (like determining vocal ranges by clef combinations and the size of the staff) we actually do in the Cogito module. This module is purely focused on output of complete music information to a music-notation language.

This module outputs in the XML format of the Music Encoding Initiative (MEI).

This means that the whole arca program is transforming an input XML document with the text and parameters into and output XML document with the text matched to music.


Write XML


enbrace Source #


:: String


-> String


-> String

contents to go in between

-> String 

Put a string between two other strings

xmltagOpen Source #


:: String

text of tag

-> String 

Create an XML opening tag, e.g., p

xmltagClose Source #


:: String

text of tag

-> String 

Create an XML closing tag, e.g., /p

xmlWrap Source #


:: String

opening tag text

-> [String]

list of contents

-> String

closing tag text

-> String 

Put something between opening and closing XML tags, specifying text of opening tag, contents, and closing tag: > xmlWrap "p" "text" "p" => ptext/p

xmlWrapBasic Source #


:: String

open and closing tag text

-> [String]

list of contents

-> String 

Put something between XML tags, with the same text for opening and closing: > xmlWrapBasic "p" "text" => ptext/p

Create elements and attributes, and elements with attributes

element Source #


:: String


-> [String]

values (list of contents, including other elements)

-> String 

Create an XML element (which may contain other elements). If you need attributes, use elementAttr.

attr Source #


:: String


-> String

single value

-> String 

Create an XML attribute

elementAttr Source #


:: String


-> [String]

list of attributes created with attr

-> [String]

values (list of contents, including other elements)

-> String 

Create an XML element that has attributes, e.g., class="foo"bar/p

Serialize our data structures to MEI

Note becomes note (pitch with syllable, or rest)

note2mei :: Note -> String Source #

Create an MEI note element from our Note datatype, converting the attributes as needed. If the Note actually contains a rest (Pitch with only a Dur in the rest range), then produce a rest element.

Conversions for Note members

meiPname :: Pitch -> String Source #

Convert Pnum to MEI pname

meiOct :: Pitch -> String Source #

Just print the octave number

meiDur :: Pitch -> String Source #

Convert Dur to MEI dur (base value of duration) and @dots (if any; omit if not)

meiAccid :: Pitch -> String Source #

Convert our Accid to MEI accid (omit if natural)

TODO Verovio will display the accidental if the accid attribute or element is present, regardless of the key signature. We would need to check the tone/key and then use accid.ges for accidentals that are in the key signature.

Conversions for lyrics

meiSyllable :: Syllable -> String Source #

If a word is a single syllable, we do not need to include @con or @wordpos; if it is more than one syllable, we include these attributes. We use a dash connector by default, and get the word position from the data in the Syllable type.

Write large groups to MEI

data ListPosition Source #

Where is this item in the list that contains it?



head of list


neither head nor last


last item in list


only item of a one-item list

positionMap :: ((ListPosition, a1) -> [a2]) -> [a1] -> [a2] Source #

Given a function that takes a ListPosition argument and a list, apply the function to the list. This allows you to treat the first and last items in the list differently.

markedEnds :: [a] -> [(ListPosition, a)] Source #

Mark a list with the positions of the items: first, body, and last. Output a list of pairs with the ListPosition and the original list item.

phrase2mei :: (ListPosition, MusicPhrase) -> String Source #

Make an XML string containing a list of note elements out of a MusicPhrase; end each phrase with barline, except for last in the list.

Leave the barline of the last phrase up to the next-higher function (end of sentence gets regular bar; end of sentence, double bar; end of section, final bar).

sentence2mei :: (ListPosition, MusicSentence) -> String Source #

Make an XML string containing all the contents of one layer out of a MusicSentence. If this is the last sentence in the section, omit the bar so the higher function calling this one can add it. Sentence ends with regular barline.

section2mei :: Arca -> (ListPosition, MusicSection) -> String Source #

A MusicSection contains all the music for one section, /for a single voice/: so combine all subdivisions into one staff and layer so this can be made part of an MEI section in chorus2mei. Include MEI 1-indexed staff number derived from VoiceName enum Put a double bar at the end of sections and a final bar at the end of the piece.

TODO: you could put more than one layer per staff if you wanted a 2-staff choirstaff (e.g., SA on one, TB on the other)

chorus2mei :: Arca -> (ListPosition, MusicChorus) -> String Source #

Take a list of sections, one per SATB voice, and create a single MEI section including all the voices. Add a final bar at the end.

meiKey :: Tone -> ToneSystem -> String Source #

Create an MEI key signature (all naturals or one flat) based on tone (key.sig attribute for use in scoreDef/staffDef)

meiKeyAttr :: Tone -> ToneSystem -> String Source #

MEI key signature as an attribute (for use in staffDef)

meiKeySigString :: Tone -> ToneSystem -> String Source #

Value for MEI key.sig: one flat if tone is mollis, no signature otherwise

meiMeter :: MusicMeter -> String Source #

Switch to select which kind of meter to use as an element

meiMeterAttr :: MusicMeter -> String Source #

Switch to select which kind of meter to use as an attribute

meiMeterModern :: MusicMeter -> String Source #

Create an MEI meter signature (using modern equivalents of Kircher's C, C3, cutC3). (meterSig with meter.count and meter.unit attributes for use in scoreDef/staffDef@)

meiMeterMensural :: MusicMeter -> String Source #

Mensural version of meiMeter. We want either "cut C", C3, or "cutC3". Verovio does not render these correctly as of 2021/07 when using the proport element (or proport.num attribute) for the number, which is the correct encoding. But putting num directly inside mensur works with Verovio.

meiMeterMensuralAttr :: MusicMeter -> String Source #

Mensural meter with proportion as a string of attributes (for use in staffDef, where the correct encoding also works with Verovio).

meiMidiTempo :: MusicMeter -> String Source #

MEI tempo element for MIDI speed: quarter-note (semiminim) beats per minute, different tempi for each mensuration

meiMidiBPM :: MusicMeter -> String Source #

MEI midi.bpm attribute appropriate for each mensuration This is how it should work, but Verovio does not accept a tempo element within staff, so I do not know how to do a tempo change mid-piece.

chorus2list :: MusicChorus -> [MusicSection] Source #

Extract a simple list of MusicSentence from the four members of a MusicChorus

Write a whole score to MEI

score2mei :: Arca -> ArkMetadata -> MusicScore -> String Source #

Convert a whole MusicScore to MEI XML. Include meter and key of first section in top-level scoreDef (TODO ?) Pass on the position in the list to the next function down.

The full MEI document

MEI Barlines

meiDoubleBar :: String Source #

Make an MEI double barline

meiFinalBar :: String Source #

Make an MEI final barline

meiBarline Source #


:: String

form string ("dbl", "end", or "" for regular barline)

-> String 

Make an MEI barLine element

String constants for XML document

_xmlHeader :: [Char] Source #

Default XML header

_meiVersion :: [Char] Source #

MEI version number

_whoami :: [Char] Source #

The "composer" (that is, the ark itself)

_Kircher :: [Char] Source #

The "inventor"

_projectDesc :: [Char] Source #

MEI project description text

midiInstrumentNum Source #


:: Int

1-indexed MIDI instrument number (e.g., 19 = Church Organ)

-> String

MEI instrDef element

MEI element for MIDI instrument number (1-indexed)

_midiInstrument :: String Source #

MIDI instrument for playback

The template

meiDocument Source #


:: String


-> String

poet/author of words

-> String

XML string with keySig element

-> String

XML string with mensur element (and proport if present)

-> String

XML string with midi.bpm attribute

-> String

XML string representing the section elements

-> String 

Plug in variables and musical content needed to boilerplate MEI document in all its baroque verbosity