Core

Core jird code.

Includes representation of music, evaluation of parsed text into music, and basic functions on music.

Language

Grammar and parsing is handled by the parsing library lark. The jird grammar is:

integer: INT
ratio: integer ["/" integer]
ratio_product: ratio ("*" ratio)*
note: ratio ":" ratio_product [":" ratio]
chord: "<" [ratio_product]* ">" ":" ratio_product [":" ratio]
?atom: note | chord | "(" part ")"
?mult_expr: [ratio "*"]* atom
?pow_expr: mult_expr | mult_expr "**" ratio -> power
part: pow_expr*
music: part (";" part)*
?start: music

%import common.INT
%import common.WS
%import common.LETTER

%ignore WS
%ignore LETTER
parse(input_string)[source]

Parse text into music.

Parameters:

input_string (str) – String containing the musical notation as text.

Returns:

Music corresponding to the text in input_string

Return type:

Piece

Examples

One note.

>>> parse("7/5:1")
([Note(frequency=7/5, cents=582.512, duration=1)],)

Two notes in succession.

>>> print_music(parse("2:1/4 6/5:1/4"))
(
  [
    Note(frequency=2, cents=1200.0, duration=1/4),
    Note(frequency=6/5, cents=315.641, duration=1/4),
  ],
),

A chord.

>>> print_music(parse("<1 7/6 16/9>:1/4"))
(
  [
    (
      Note(frequency=1, cents=0.0, duration=1/4),
      Note(frequency=7/6, cents=266.871, duration=1/4),
      Note(frequency=16/9, cents=996.09, duration=1/4),
    ),
  ],
),

Music representation

class Unevaluated[source]

Base class for unevaluated quantities.

abstract evaluate()[source]

Calculate actual value from unevaluated representation.

class RatioProduct(ratios=())[source]

Unevaluated product of ratios.

evaluate()[source]

Calculate product of ratios.

class Power(base, exponent_numerator, exponent_denominator)[source]

Unevaluated power.

The power is of the form b**(m/n) where b, m, and n are integers. Used for example to make number of steps in equal temperaments more obvious, e.g. 2**8/12 rather than 2**2/3.

evaluate()[source]

Calculate value of power.

class MidiNote(pitch, bend, ticks, velocity)[source]

Midi representation of a musical note.

pitch

Midi pitch number. Between 0 and 127.

Type:

int

bend

Midi pitch bend number. Between 0 and 16383.

Type:

int, optional

ticks

Number of midi ticks that the note lasts.

Type:

int

velocity

Midi note velocity (often mapped to loudness by synths). Between 0 and 127.

Type:

int

class Note(frequency, duration, volume=Fraction(1, 1))[source]

Individual musical note.

A note here is characterized by its frequency (giving its pitch), its duration (giving how long it is played for), and its volume (giving how loud it sounds). Frequency, duration, and volume are given as a fraction of basic units which are left unspecified.

frequency

Frequency ratio, e.g. 5/4. Real frequency of note is this frequency ratio multiplied by the basic frequency \(f_0\).

Type:

RatioProduct or Fraction or Power

duration

Duration of note as a fraction of a basic time unit \(t_0\). The basic time unit can be chosen freely, e.g. to be the length of one bar or one whole note.

Type:

RatioProduct or Fraction

volume

Volume of note as a fraction of a reference volume. The reference volume 1/1 is mapped to midi volume 64.

Type:

RatioProduct or Fraction

Notes

A rest is represented by a note with zero frequency.

property cents

Calculate number of cents in the interval from 1 to the note’s frequency.

Returns:

Number of cents in interval from 1 to frequency rounded to three decimal places. Returns nan if the note has zero frequency (corresponding to a rest).

Return type:

float

Examples

There are about 700 cents in a just perfect fifth.

>>> note = parse("3/2:1")[0][0]
>>> note.cents
701.955
to_midi(*, f0, pitch_bend_range)[source]

Midi representation of note.

Parameters:
  • f0 (float) – Basic frequency for computing real frequency of note.

  • pitch_bend_range (int) – Number of semitones to assume max pitch bend corresponds to.

Returns:

Midi representation of note.

Return type:

MidiNote

Temperament

temper_note(note, *, edo)[source]

Temper single note.

Approximates frequency of note by one of the frequencies obtained by splitting the octave into edo parts.

Parameters:
  • note (Note) – Note to temper.

  • edo (int) – Number of Equal Divisions of the Octave to select the tempered frequency from. For example edo = 12 is the common equal temperament. Other popular values are 19, 31, and 53, but anything is possible and perhaps interesting.

Returns:

Closest approximation to note in a system of edo equal divisions of the octave.

Return type:

Note

Examples

Consider a minor third (frequency ratio 6/5):

>>> note = parse("6/5:1")[0][0]
>>> note
Note(frequency=6/5, cents=315.641, duration=1)

Tempering with twelve notes gives three ordinary semitones, exactly 300 cents.

>>> temper_note(note, edo=12)
Note(frequency=2**3/12, cents=300.0, duration=1)

Tempering with nineteen notes gives a very close approximation to the just frequency.

>>> temper_note(note, edo=19)
Note(frequency=2**5/19, cents=315.789, duration=1)

Notes

A ratio product has each term tempered separately (rather than first multiplying terms then tempering). This is to preserve the tempered intervals within a chord when its frequency is multiplied. For example <1 5/4 11/8>:1 is tempered in 12EDO to 0, 4, 6 steps, but 80/81*<1 5/4 11/8>:1 would be tempered to 0, 4, 5 steps. From listening to automatically tempered just intonation music (e.g. megamorsel in music dir in this repo), this shifting of chord quality can sound strange. Tempering each term in a product separately makes sure that the intervals within the chord remain unchanged by multiplication.

temper(music, *, edo)[source]

Temper all notes in music.

Parameters:
  • music (Piece) – Music to temper.

  • edo (int, optional) – Number of Equal Divisions of the Octave to use for tempering. No tempering done if edo is None.

Returns:

Tempered version of music.

Return type:

Piece

Interval tables

print_interval_table(music)[source]

Print interval table built from all frequencies in music.

Parameters:

music (Music) – Music containing frequencies to build intervals from.

Examples

>>> print_interval_table(parse("<1 7/6 4/3>:1"))

         1  7/6  4/3
     ---------------
  1  |   1  7/6  4/3
7/6  | 6/7    1  8/7
4/3  | 3/4  7/8    1
interval_table(frequencies)[source]

Find interval between each pair of frequencies in frequencies.

Parameters:

frequencies (iterable of Ratio) – Frequencies to find intervals between.

Returns:

All intervals between frequencies.

Return type:

list of list of Ratio

Examples

>>> table = interval_table([Fraction(1), Fraction(5, 4), Fraction(3, 2)])
>>> for row in table: print(row)
[Fraction(1, 1), Fraction(5, 4), Fraction(3, 2)]
[Fraction(4, 5), Fraction(1, 1), Fraction(6, 5)]
[Fraction(2, 3), Fraction(5, 6), Fraction(1, 1)]

Useful functions

all_frequencies(music)[source]

Find all frequencies in music.

Parameters:

music (Music) – Music to find frequencies in.

Returns:

List of all frequencies in music sorted from lowest to highest.

Return type:

list of [Fraction or float]

Examples

Any products are evaluated so frequencies are given in their simplest form.

>>> music = parse("1:1 5/4:1 4/3*5/4:1")
>>> all_frequencies(music)
[Fraction(1, 1), Fraction(5, 4), Fraction(5, 3)]

This can be used to find a scale in which all notes in music can be found.

>>> music = parse("10/9*<1 6/5 9/5>:1 3/2*<1 5/4>:1")
>>> all_frequencies(music)
[Fraction(10, 9), Fraction(4, 3), Fraction(3, 2), Fraction(15, 8), Fraction(2, 1)]
total_duration(music)[source]

Find total duration of music.

Takes largest duration if music contains simultaneous pieces of different duration.

Parameters:

music (Music) – Music to find duration of.

Returns:

Total duration of music.

Return type:

Number

Examples

>>> music = parse("7/6:1/4 4/3:1/4")
>>> total_duration(music)
Fraction(1, 2)
>>> music = parse("2:1 3/2:1; 1/2:2")
>>> total_duration(music)
Fraction(2, 1)
height(music)[source]

Measure of number of simultaneous notes in music.

Used for example to assign enough midi channels to each part in jird.midi.music_to_midi_file().

Parameters:

music (Music) – Music to measure height of.

Returns:

Measure of simultaneous height of music.

Return type:

int

Examples

>>> height(parse("<1 5/4 3/2 7/4>:1"))
4
>>> height(parse("<1 5/4 3/2 7/4>:1; 1/2:1"))
5
>>> height(parse("1:1/4 9/8:1/4 5/4:1/2; 1/2:1/2 3/4:1/2"))
2
lowest(music)[source]

Find lowest frequency in music.

Parameters:

music (Music) – Music to containing frequencies to consider.

Returns:

Lowest frequency in music.

Return type:

Number

print_music(music, level=0)[source]

Pretty print music.

Parameters:
  • music (Music) – Music to be printed.

  • level (int) – Indentation level. Defaults to 0. Used to indent nested pieces of music when print_music is called recursively.

apply_to_notes(music, f)[source]

Apply a function f to each note in a Music object.

Parameters:
  • music (Music) – Music containing the notes to apply f to.

  • f (function) – Function to apply to each note.

Returns:

The music object with f applied to each note.

Return type:

Any

Examples

Extract the cents for each note in some music.

>>> music = parse("5/4:1 <1 3/2>:1")
>>> apply_to_notes(music, lambda x: x.cents)
([386.314, (0.0, 701.955)],)

Double duration of each note.

>>> music = parse("7/6:1 4/3:1")[0]
>>> music
[Note(frequency=7/6, cents=266.871, duration=1), Note(frequency=4/3, cents=498.045, duration=1)]
>>> f = lambda x: Note(frequency=x.frequency, duration=evaluate(Fraction(2) * x.duration))
>>> apply_to_notes(music, f)
[Note(frequency=7/6, cents=266.871, duration=2), Note(frequency=4/3, cents=498.045, duration=2)]
evaluate(quantity)[source]

Evaluate a quantity.

For quantities which are Unevaluated call their evaluate to force evaluation.

Parameters:

quantity (Unevaluated or Number) – quantity to evaluate.

Returns:

Evaluated quantity

Return type:

Number

Examples

>>> quantity = RatioProduct((Fraction(5, 4), Fraction(6, 5)))
>>> quantity
5/4*6/5
>>> evaluate(quantity)
Fraction(3, 2)
>>> evaluate(Fraction(7, 6))
Fraction(7, 6)

Transformation from AST to Music

class NoteTransformer(visit_tokens=True)[source]

Transformer to convert an abstract syntax tree (AST) into a Music object.

Each method is called on the corresponding node in the AST, so methods have the same names as elements of the grammar. The transformer works up the tree from the bottom. For more information see the Lark docs here.

NoteTransformer.integer(children)[source]

Convert token (subclass of string) for integer to actual integer.

Parameters:

children (list of Token) – List of exactly one string representing an integer.

Returns:

Integer corresponding to single element of children.

Return type:

int

NoteTransformer.ratio(children)[source]

Convert pair of integers representing a ratio into the actual ratio.

The ratio is returned as a RatioProduct in order to leave any later multiplications in a factored form, e.g. 5/4*3/2 will remain 5/4*3/2 rather than 15/8. This is because the factorization often comes from ‘factoring out the root note’ and so is musically useful to know.

Parameters:

children (list of (int or None)) – The numerator and denominator of the ratio. If the denominator is None the numerator is returned. This allows handling both “3/2” and “2” as ratios.

Returns:

Ratio corresponding to integers in children.

Return type:

RatioProduct

NoteTransformer.ratio_product(children)[source]

Form product of ratios.

Parameters:

children (list of RatioProduct) – Ratios to multiply.

Returns:

Product of ratios in children.

Return type:

RatioProduct

NoteTransformer.mult_expr(children)[source]

Multiply frequencies of notes in music by a ratio.

Parameters:

children (list of RatioProduct or Music) – Last element is the Music containing the notes to multiply the frequencies of. Preceding elements are ratios to multiply frequencies in the music by.

Returns:

Music with its note frequencies multiplied by given ratios.

Return type:

Music

NoteTransformer.power(children)[source]

Multiply note volume by exponent.

Parameters:

children (list of Music or RatioProduct) – Two element list of music and exponent.

Returns:

Music with its volume multiplied.

Return type:

Music

NoteTransformer.note(children)[source]

Convert frequency, duration, and volume into corresponding note.

Parameters:

children (list of RatioProduct) – Three element list containing frequency, duration, and volume of a note. Volume can be None.

Returns:

List containing a single Note of given frequency, duration, and volume. Volume is defaulted to one.

Return type:

list of Note

NoteTransformer.chord(children)[source]

Convert list of frequencies, duration, and volume into the corresponding chord.

Parameters:

children (list of RatioProduct) – List containing the frequencies of the notes in the chord along with the duration of the chord as the second last element and the volume as the last element. Volume can be None.

Returns:

List containing a single tuple of notes making up the given chord.

Return type:

list of tuple of Note

NoteTransformer.part(children)[source]

Combine musics sequentially by forming a single list from them.

Parameters:

children (list of list of Music) – List of individual musics to combine sequentially.

Returns:

Single list of all musics joined together.

Return type:

list of Music

NoteTransformer.music(children)[source]

Combine musics simultaneously by forming a tuple of them.

Parameters:

children (list of Music) – List of musics to combine simultaneously.

Returns:

Tuple of individual musics to be played simultaneously.

Return type:

tuple of Music