Kodou's interface consists of a class Part and a main processing function kodou. A configuration file .kodou can be used to set some system variables.


Part

Signature: (events, metadata={})

A Part object can be thought of as a single instrument.

  • events: a dictionary which accepts following keys:

    • "notes": a collection (monophon Part) or a collection of collections (polyphon Part) of either MIDI key numbers or Lilypond note names (as strings, including Lilypond rests "r" and spacer rests "s", more on notes and rests) or a sorted collection (list or tuple) of them as chords. Please note that MIDI key numbers below or beyond the MIDI range (0, 127) can also be notated (if no midi output needed). Microtonal notes smaller than quarter tones are supported according to the specifications of Ekmelily. Please note that ekmelily is loaded into the ly file by default. It is possible to prevent it from being loaded by setting the load_ekmelily key to no in the .kodou configuration file.

    • "beats": a collection (monophon Part) or a collection of collections (polyphon Part) of integers or floats.

    • "durations": a dictionary with keys being beats getting the new durations and values being the durations.

  • metadata: a dictionary which accepts zero (default) or more of the following keys. If Part is polyphon, a specific metadata can be assigned to a single voice by wrapping that metadata's dictionary inside an outer dictionary with keys of type integer representing the voice number (starting from 0) and values being the metadata dictionary. If Part is polyphon and no voice assignment takes place, the metadata dictionary will be applied to all voices likewise.

    • "who" : a string. This should be a unique id and will be used in the ly file as the variable name holding the processed Part instance. If "who" is not specified, kodou will generate a random id.

    • "what": a dictionary which accepts the following two keys:

      • "name": value should be a string used as the instrument name.
      • "abbr": value should be a string used as the abbreviation for the instrument name.
    • "staff": a dictionary which accepts following key/value pairs:

      • "n": an integer specifying the number of systems. Defaults to 1.
      • "types": a string specifying the type of the staff which can be any of the following names: "basic", "drum", "rhythmic", "tablature", "mensural", "vaticana", "gregorian", or a dictionary (in case the Part instance is polyphonic) with keys being integers specifying the number of each voice (starting from 0) and values being one of the above staff names.
      • "bind": a string specifying the type of bracket used for grouping the stave together. It can be one of: "basic", "group", "choir", "grand", "piano"
    • "timesig": a dictionary which accepts following key/value pairs:

      • key: can be an integer specifying the beat (started from 0) before which the new time signature should appear, or a two element tuple specifying the time interval to which the time signature should be applied. If key is a tuple, time signature will be set according to the time interval specified by the tuple, i.e. time signature changes will occure at proper points if the time interval divided by the numerator of the time signature yields a rest. Overlapping time signature intervals will be processed, setting the current time signature back to the previous one if the current one lies inside the range of the previous time signature. If key is an integer, the time signature will be valid up to the next integer key. This will overwrite all future time intervals (please note that this also doesn't allow any future time interval specifications for setting time signatures. Consider using tuples as _key_s instead).
      • value: a two element tuple or list representing the numerator and the denominator of the time signature. Defaults to (4, 4). (timesig example )
    • "clef": a dictionary which accepts following key/value pairs:

      • key: can be an integer or a float specifying the beat where the clef change should take place, or a two element tuple specifying the time interval to which the clef change should be applied. Overlapping time intervals will be processed, setting the current clef back to the previous one if the current one lies inside the range of the previous clef. If the key is not a tuple (i.e. is an integer or a float), the clef will be valid up to the next integer or float key. This will overwrite all future time intervals (please note that this also doesn't allow any future time interval specifications for setting clefs. Consider using tuples as _key_s instead).
      • value: can be one of the following strings: "C", "F", "G", "G2", "GG", "alto", "altovarC", "baritone", "baritonevarC", "baritonevarF", "bass", "blackmensural-c1", "blackmensural-c2", "blackmensural-c3", "blackmensural-c4", "blackmensural-c5", "french", "hufnagel-do-fa", "hufnagel-do1", "hufnagel-do2", "hufnagel-do3", "hufnagel-fa1", "hufnagel-fa2", "kievan-do", "medicaea-do1", "medicaea-do2", "medicaea-do3", "medicaea-fa1", "medicaea-fa2", "mensural-c1", "mensural-c2", "mensural-c3", "mensural-c4", "mensural-c5", "mensural-f", "mensural-g", "mezzosoprano", "moderntab", "neomensural-c1", "neomensural-c2", "neomensural-c3", "neomensural-c4", "neomensural-c5", "percussion", "petrucci-c1", "petrucci-c2", "petrucci-c3", "petrucci-c4", "petrucci-c5", "petrucci-f", "petrucci-f2", "petrucci-f3", "petrucci-f4", "petrucci-f5", "petrucci-g", "petrucci-g1", "petrucci-g2", "soprano", "subbass", "tab", "tenor", "tenorG", "tenorvarC", "treble", "varC", "varbaritone", "varpercussion", "vaticana-do1", "vaticana-do2", "vaticana-do3", "vaticana-fa1", "vaticana-fa2", "violin". Transposing clefs can be written in the form "clef+N", "clef+[N]" or "clef+(N)" for a clef transpoing N diatonic steps upwards, or "clef-N", "clef-[N]" or "clef-(N)" for a clef transpoing N diatonic steps downwards. Defaults to "treble". (clef example)
    • "notehead": a dictionary which accepts following key/value pairs:

      • key: can be an integer or a float specifying the beat where the notehead should appear, or a two element tuple specifying the time interval to which the notehead should be applied. Notes should exist on specified beats, or else the notehead will be applied to the nearest existing note. Overlapping time intervals will be processed, setting the current notehead back to the previous one if the current one lies inside the range of the previous notehead. If the key is not a tuple (i.e. an integer or a float), the notehead will be valid up to the next integer or float key. This will overwrite all future noteheads specified in a time interval (please note that this also doesn't allow any future time interval specifications for setting noteheads. Consider using tuples as _key_s instead).
      • value: can be one of the following strings: "default", "altdefault", "baroque", "neomensural", "mensural", "petrucci", "harmonic", "harmonic-black", "harmonic-mixed", "diamond", "cross", "xcircle", "triangle", "slash", "do", "re", "mi", "fa", "#f", "la", "ti". Defaults to "default". (notehead example)
    • "barline": a dictionary which accepts following key/value pairs:

      • key: can be an integer or a float specifying the beat where the barline should appear.
      • value: a string which can be one of the Lilypond barlines or a new barline definition with the following syntax: "bartype => end begin span" where each of end, begin or span can be one of barline elements supported by Lilypond or an underscore _ as a placeholder for an invisible barline element. Once a new barline has been defined, it can be used subsequently just as built-in barlines. (for more information about Lilypond barlines and new barline definitions please consult the barline documentation page). (barline examples)
    • "dynamic": a dictionary which accepts following key/value pairs:

      • key: can be an integer or a float specifying the beat where the absolute dynamic should appear, or a two element tuple specifying the time interval to which the relative dynamic (crescendo or decrescendo) should be applied. Overlapping or crossing dynamics will be processed accordingly. If no notes exist on specified beats, rests are created to accommodate the dynamics.
      • value: a string which can be any of the Lilypond dynamics or a new absolute dynamic definition with the following syntax: "dynamic_name => args", where dynamic_name is the name of the variable used as the new dynamic and args are any white-space-seperated combination of ascii characters and the new dynamic (an arbitrary combination of the following characters: f, m, p, r, s, z) prefixed by an identifier D. Once a new dynamic has been defined, it can be used subsequently just as built-in dynamics. (dynamic examples)
    • "legato": a dictionary which accepts following key/value pairs:

      • key: one of the strings: "solid", "halfsolid", "dashed", "halfdashed" or "dotted".
      • value: a list or a tuple of a two element tuple of integers or floats specifying the start and the end beats of the legato (exclusive of the ending beat). If no notes exist on the specified starting beat, a rest is created to accommodate the legato. Please note that Lilypond allows a maximum of two overlapping legatos. From the documentation page: Simultaneous or overlapping slurs are not permitted, but a phrasing slur can overlap a slur. This permits two slurs to be printed at once. Also: Simultaneous or overlapping phrasing slurs are not permitted. The legato metadata values will be processed according to these aspects. (legato example)
    • "articulation": a dictionary which accepts following key/value pairs:

      • keys: can be an integer or a float specifying the beat where the articulation should take place, or a two element tuple specifying the time interval to which the articulation should be applied.
      • values: can be one or more of the following strings:"accent", "espressivo", "marcato", "portato", "staccatissimo", "staccato", "tenuto", "prall", "prallup", "pralldown", "upprall", "downprall", "prallprall", "lineprall", "prallmordent", "mordent", "upmordent", "downmordent", "trill", "turn", "reverseturn", "shortfermata", "fermata", "longfermata", "verylongfermata", "upbow", "downbow", "flageolet", "open", "halfopen", "lheel", "rheel", "ltoe", "rtoe", "snappizzicato", "stopped", "segno", "coda", "varcoda", "accentus", "circulus", "ictus", "semicirculus", "signumcongruentiae", ">", "^", "_", "!", ".", "-", "+", "<>". Except with fermatas and repeat signs a note should exist on specified beats or no articulations will be shown. Please note that as soon as rests have been created to show fermatas and repeat signs if specified beats are missing, these new beats may also accept other articulations. (articulation example)

kodou

Signature: (score, metadata={}, file_name="kodou", path="/tmp", dot_kodou="~/.kodou", outputs=["pdf"], view=True)

The main processing function with following parameters:

  • score: can be a Part instance or a list/tuple of Part instances.
  • metadata: global metadata which will be applied to all Part instances in the score parameter. Part's metadata will overwrite this parameter.
  • file_name: will be used as the name for all output files.
  • path: where to save the output files.
  • dot_kodou: the path to the .kodou file.
  • outputs: a collection of output format strings. Can be any of: "pdf" (default), "svg", "jpg", "midi", "mid".
  • view: whether to show the PDF file using the pdf viewer specified in the .kodou file after compilation or not. Defaults to True.

.kodou

The content of this configuration file will be used for setting some kodou and system variables. The path to this file defaults to /home/user/.kodou and can be reset in the dot_kodou parameter of the kodou function.
The syntax is key = value. Lines starting with a pound sign (#) are comments.
As of this writing (23. Aug. 2018) following keys can be set:

  • ly_version: Lilypond version. Defaults to 2.21.0.
  • ly_language: Lilypond language. Defaults to deutsch.
  • viewer: path to the PDF-viewer binary. Defaults to /usr/bin/evince.
  • ly_exe: path to the Lilypond binary. Defaults to /usr/local/bin/lilypond.
  • ly_paper_size: paper format used by Lilypond. Defaults to quarto. More formats
  • ly_staff_size: staff size used by Lilypond. Defaults to 14.
  • load_ekmelily: whether the ekmelily system should be loaded for microtonal notation (yes) or not (no). Defaults to yes.

Metadata examples

from kodou import * applies to all of the code snippets and will be omitted in the following.

timesig:

The following metadata:


kodou(Part(events={"notes": range(60, 84),
                   "beats": range(24)},
           metadata={"timesig": {
               (1, 12): (3, 8),
               (4, 7): [2, 32],
               17: (5, 4),
               (19, 20): (1, 8)
           }}))

will be processed in the following way:

  1. beat 0 (4/4, default time signature) to beat 1 results in a 1/4
  2. beat 1 (3/8) to beat 4 results in a 3/8
  3. beat 4 (2/32) to beat 7 results in one 2/32 and a remainder of 1/32
  4. beat 7 resets to 3/8 till beat 12, resulting in one 3/8 and a remainder of 2/8
  5. beat 12 resets to 4/4 (default time signature) till beat 17, resulting in one 4/4 and a remainder of 1/4
  6. from beat 17 on 5/4

and produce:

timesig_out


clef:


kodou(Part(events={"notes": range(60, 84),
                   "beats": range(24)},
           metadata={"clef": {
               (0.5, 8+1/3): "alto",
               (4, 6): "tenor",
               8+2/3: "treble+9",
               13.5: "treble-8",
               16: "soprano"
           }}))

clef_out


notehead:

The following metadata:


kodou(Part(events={"notes": range(60, 84),
                   "beats": range(24)},
           metadata={"notehead": {
               (.5, 8+1/3): "mensural",
               (4, 6): "harmonic",
               8+2/3: "cross",
               13.5: "triangle",
               16: "xcircle"
           }}))

will be processed in the following way:

  1. nearest existing note to the beat 0.5 is on beat 0 (0.5 will be rounded downwards), from beat 0 to beat 4 are "mensural"
  2. from beat 4 to 6 are "harmonic"
  3. nearest existing note to the beat 8.333333333333334 is on beat 8, from beat 6 to beat 8 are set back to "mensural"
  4. nearest existing note to the beat 8.666666666666666 is on beat 9, beat 8 to beat 9 is set back to "default"
  5. nearest existing note to the beat 13.5 is on beat 13 (again 0.5 will be rounded downwards), from beat 9 to beat 13 are "cross"
  6. from beat 13 to beat 16 are "triangle"
  7. from beat 16 onwards are "xcircle"

and produce:

notehead_out


barline:


def merge_dicts(d1, *ds):
    """merge two or more dictionaries"""
    for d in ds:
        d1.update(d)
    return d1

kodou(Part(events={"notes": range(60, 84),
                   "beats": range(24)},
           metadata={"barline": merge_dicts(
               # make the default barlines invisible
               {beat: "" for beat in range(3, 24, 4)},
               # set new barlines after each third beat
               {beat: "!" for beat in range(2, 24, 3)},
               # and a closing barline at the end
               {23: "|."}
           )}))

barline_out


kodou(Part({"notes":[range(48, 84)] * 3,
            "beats": [range(36)] * 3},
           {"staff": {"n": 3, "bind": "grand"},
            "barline": {
                # define new barlines and use them
                0: {n * 4 - 1: "= => = _ _" for n in range(1, 10)},
                1: {n * 4 - 1: "] => ] _ _" for n in range(1, 10)},
                2: {n * 4 - 1: "[ => [ _ _" for n in range(1, 10)}
            }}))

barline_define_out


dynamic:


kodou(Part({"notes": range(60, 72),
            "beats": [n * .5 for n in range(12)]},
           {"dynamic": {(0, 5.5): "<"}}))

dynamic_out_1


def merge_dicts(d1, d2):
    """merge two dictionaries"""
    d1.update(d2)
    return d1

kodou(Part(events={"notes": [range(60, 72), range(72, 60, -1)] * 5,
                   "beats": [range(12)] * 10},
           metadata={"staff": {"n": 10, "bind": "grand"},
                     "dynamic": merge_dicts(
                         {v: {3: "p", 7: "sf", (0, 11): ">"}
                          for v in range(0, 10, 2)},
                         {v: {2: "sf", (0, 11): "<"}
                          for v in range(1, 10, 2)})
           }))

dynamic_out_2


kodou(Part({"notes": range(60, 72),
            "beats": range(12)},
           {"dynamic": {(0, 11): "<",
                        (4, 8): ">",
                        6: "ppp"}}))

dynamic_out_3


def merge_dicts(d1, *ds):
    """merge two or more dictionaries"""
    for d in ds:
        d1.update(d)
    return d1

kodou(Part(events={"notes": range(60, 72),
                   "beats": range(12)},
           metadata={"dynamic": merge_dicts(
               # define some new absolute dynamics
               {0: "starf => * Drfz *"},
               {1: "roundp => ( Dp )"},
               # now they can be accessed just as any other dynamics
               {beat: "starf" for beat in range(2, 12, 2)},
               {beat: "roundp" for beat in range(3, 12, 2)}
           )}))

dynamic_out_4


kodou(Part(events={"notes": range(60, 84),
                   "beats": range(24)},
           metadata={"dynamic": {
               (0, 6): "<",
               (2, 5.1): ">",
               5.5: "sf",
               4: "mp",
               (6.5, 23): "dim",
               (10+2/3, 15.5): "cr",
               13+3/7: "p",
               23: "rfz"
           }}))

dynamic_out


legato:


kodou(Part(events={"notes": range(60, 84),
                   "beats": range(24)},
           metadata={"legato": {
               "solid": ((1, 15.5),
                         (3+1/3, 5),
                         (5, 7)),
               "halfdashed": [(7, 12.5),
                              (12.5, 17)],
               "dotted": ((8, 9),
                          (16, 23))
           }}))

legato_out


articulation:


kodou(Part(events={"notes": range(60, 72),
                   "beats": range(12)},
           metadata={"articulation": {
            3: (">", "."),
        (1, 7): "staccatissimo",
        (4, 6): ["prall", "^", "trill", "turn"],
        10.5: ("fermata", "trill"),
        11: "<>"
       }}))

articulation_out