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 processes data from the ark to convert it into music (cogito, Latin, "I think").


This module receives input from the Lectio module in the form of a single list of one or more LyricSections, which contain the parsed text to be set to music and the parameters for setting it.

The module uses Kircher's rules to pull the appropriate data from the Arca musarithmica, that is, from the Arca built by the Aedifico module. It uses the Fortuna module to get lists of random permutation indices.

The main function is makeMusicScore, which applies all the necessary rules to select music data from the ark for each phrase of text, using the random permutations when a free choice would otherwise be needed. It takes the numerals and rhythmic symbols from Kircher's pinakes (rods); converts the numerals to pitches according to the tone, and combines the pitches and rhythms (accounting for rests as well).

The module creates the MusicScore data structure which contains all the data for the music in hierarchical sections that the Scribo module will convert to MEI XML.

Text underlay

Pitches and syllables are stored together in the Note type. In Syntagma I (simple syllabic counterpoint), we store one syllable for each note, skipping rests.

In Syntagma II, though, for florid counterpoint, Kircher does not specify how to underlay the text, and the settings have variable numbers of notes in the different voices and between different permutations in the same pinax. The underlay must be left to the human performer, then, and so we just lump all the lyrics for each phrase and put them under the first syllable as a textual incipit.

MEI vs. Lilypond output

We previously set up this module to feed data into the Scribo.Lilypond module, using the main function getSymphonia. It treated pitches and lyrics completely separately, which worked in Syntagma I but not in Syntagma II. These functions are archived in the test/ directory.


Match pitches and rhythms

Get music data for a single voice

zipFill Source #


:: [a]

list 1

-> [b]

list 2

-> (a -> Bool)


-> b

substitute element to use instead of list 2 item, if list 1 item meets test

-> [(a, b)]

return list of pairs made of list 1 and 2, in item order

Take two lists and zip them together, that is, make a new list of ordered pairs constructed from sequential items of the two lists, BUT: if an element in the first list satisfies a given test, make a pair of that element and a sustitute element (sub) instead of the corresponding element from the second list.

pair2Pitch Source #


:: ToneList 
-> ToneSystem 
-> Tone 
-> (Dur, Int)

duration and pitch number 0-7

-> Pitch 

Make a pitch from duration and pitch number. Start with dummy octave number; we'll set it later using stepwiseVoiceInRange. Adjust the pitch for tone (pnumAccidInTone).

isBflatInSignature :: Pnum -> Accid -> Tone -> ToneSystem -> Bool Source #

Is this note a B flat, and if so, is the flat already in the key signature?

voice2octave :: Num p => VoiceName -> p Source #

Get the right starting octave range for each voice type voice2octave :: VoiceName -> Int

From input parameters to music

ark2voice Source #


:: Arca

ark data structure

-> ArkConfig

we pass this along to getVoice and getRperm; we use the Tone for pair2Pitch

-> PenultLength

penultimate syllable length

-> Int

syllable count

-> Int

line count

-> VoiceName

voice name enum

-> Perm

contains random index for voice and rhythm permutation

-> Voice 

Central functions of the ark: given all parameters required by Kircher (style, meter, syllable count, penultimate syllable length), select a voice permutation (Kircher's number tables) from the appropriate part of the ark and match it to a rhythm permutation (his tables of note values).

Return a Voice with the pitches for a single voice part.

We use getVoice and getRperm from the Aedifico module.

Because the rhythms can include rest, we have to match up pitches and rhythms accordingly using zipFill with the test isRest.

Methods to create and populate data structures for music composed by the

makeSyllables :: Verbum -> [Syllable] Source #

Take a Verbum read from the input file and turn it into a list of Syllables for storage in Notes. Record the syllable's position within the word.

blankSyllable :: Syllable Source #

Just a blank syllable for filler when needed

makeMusicPhrase :: Arca -> ArkConfig -> VoiceName -> LyricPhrase -> Perm -> MusicPhrase Source #

Compose the music for a whole LyricPhrase with one permutation from the ark, and package it into a MusicPhrase. Note that this is for a single voice only, not the four SATB voices.

Line up pitches and syllables, skipping rests. In Syntagma I, line up text and notes syllabically (one syllable per note); in syntagma II (florid), lump the text into a single syllable and put it as an incipit text at the beginning of the phrase. (See module description for why Kircher's specification makes this is necessary.)

makeMusicSentence :: Arca -> ArkConfig -> VoiceName -> LyricSentence -> SentencePerm -> MusicSentence Source #

Compose music for a LyricSentence for a single voice.

makeMusicSection :: Arca -> LyricSection -> SectionPerm -> VoiceName -> MusicSection Source #

Put together all the music information for one LyricSection, for a single voice.

  • For a single voice:

    • extract ArkConfig for whole section
    • for each sentence in section:

      • extract list of perms, one per phrase
      • extract list of lyric phrases
      • apply same ArkConfig
    • for each phrase in sentence:

      • look up vperm according to config and perm

        • (for some pinakes, choose column by stanza = section num)
      • look up rperm according to config and perm

        • (for syntagma II, use same perm)
      • convert vperm nums to pitch names
      • (adjust pitches)
      • make Pitches: match pitches and rhythms, accounting for rests
      • match Notes: match each Pitch with PhraseVerbumSyllable according to syntagma
      • return a MusicPhrase
    • inside a MusicSentence
  • inside a MusicSection

makeMusicChorus :: Arca -> LyricSection -> SectionPerm -> MusicChorus Source #

Compose music for all four SATB voices for one LyricSection. TODO experimental: also adjust for musica ficta

makeMusicScore :: Arca -> [LyricSection] -> [SectionPerm] -> MusicScore Source #

Compose the music for the whole document as a MusicScore, pulling the data from the Arca.