API

loeric.__main__.sync_thread(groover: Groover, sync_port_in: BaseInput, out: BaseOutput) None

Handle MIDI start, stop, songpos and tempo messages.

loeric.loeric_utils.get_chord_pitches(harmony: int) array

Return the pitches of a major or minor chord in semitones from the root. :param harmony: the chord. Values 0-11 indicate a major chord. Values 12-23 indicate a minor chord. Values 24-35 indicate a diminished chord. Values 36-48 indicate an augmented chord.

Returns:

the pitches that are part of the input chord.

loeric.loeric_utils.get_ports(input_number: int = None, output_number: int = None, list_ports: bool = False, create_in: bool = False, create_out: bool = False)

Return the port names associated to the given indexes. If listing ports, only input and output port names will be printed.

Parameters:
  • input_number – the input port index.

  • output_number – the output port index.

  • list_ports – whether or not to list port names and return.

  • create_in – whether or not a new input will be created.

  • create_out – whether or not a new output will be created.

Returns:

a tuple (input, output) containing the input and output port names.

loeric.loeric_utils.get_root(key_signature: str) int

Return the tonic of a given key signature.

Parameters:

key_signature – the key signature in the following format: [A-G](#|b)?m?

Returns:

the toinc of the key signature.

loeric.loeric_utils.is_note(msg: Message) bool

Check if a midi event is a note event (either note-on or note-off).

Parameters:

msg – the message to check.

Returns:

True if the message is a note event.

loeric.loeric_utils.is_note_off(msg: Message) bool

Check if a midi event is to be considered a note-off event, that is:

  • its type is “note-off” or

  • its type is “note-on” and it has 0 velocity.

Parameters:

msg – the message to check.

Returns:

True if the message is a note on event.

loeric.loeric_utils.is_note_on(msg: Message) bool

Check if a midi event is to be considered a note-on event, that is:

  • its type is “note-on”;

  • it has non-zero velocity.

Parameters:

msg – the message to check.

Returns:

True if the message is a note on event.

class loeric.groover.Groover(tune: Tune, bpm: int = None, midi_channel: int = 0, transpose: int = 0, diatonic_errors: bool = True, random_weight: float = 0, human_impact: float = 0, seed: int = 42, config_file: str = None, intensity_control: int = 1, human_impact_control: int = 11, syncing: bool = False)

The class responsible for playback, ornamentation and human interaction.

__init__(tune: Tune, bpm: int = None, midi_channel: int = 0, transpose: int = 0, diatonic_errors: bool = True, random_weight: float = 0, human_impact: float = 0, seed: int = 42, config_file: str = None, intensity_control: int = 1, human_impact_control: int = 11, syncing: bool = False)

Initialize the groover class by setting user-defined parameters and creating the contours. Any parameters set on class instatiation that are also present in the configuration file will be overwritten. To preserve command line arguments, omit the corresponding fields from the configuration file.

Parameters:
  • tune – the tune that will be performed.

  • bpm – the user-defined tempo in bpm for the tune.

  • midi_channel – the midi output channel for all note messages. Drone messages will be sent on midi_channel + 1 if not specified otherwise in the configuration.

  • transpose – the number of semitones by which to transpose the tune.

  • diatonic_errors – whether or not error generation should be quantized to the tune’s mode.

  • random_weight – the weight of the random component in contour generation.

  • human_impact – the initial weight of the external control signal.

  • seed – the random seed of the performance.

  • config_file – the path to the configuration file (must be a JSON file).

  • syncing – whether or not synchronization with multiple LOERIC istances is active.

__weakref__

list of weak references to the object

_add_drone(notes: array, drones: array, is_note_on: bool) array

Add drones to each note in input.

Parameters:
  • notes – the notes to add a drone to.

  • drone – the drone notes to add.

  • is_note_on – whether this is a note on message or not.

:return the input notes, with an added drone.

_apply_swing() float

Apply a p:1 swing by offsetting the start time of the next note, where p is user defined. e.g. p=1: straight eight notes; p=2: triplet swing

property _current_swing: float
Returns:

the current swing amount given the bound countour.

property _current_velocity: int
Returns:

the current velocity given the value of the velocity contour.

property _cut_duration: float
Returns:

the duration of a cut note.

_duration_of(time: float) float

Calculate the duration in seconds in the current tempo of the input duration, given in tune tempo.

Parameters:

time – the input time value in seconds, given the original tempo.

Returns:

the new duration of the input time value in seconds.

property _eight_duration: float
Returns:

the duration of a eight note in seconds at original tune tempo.

_instantiate()

Generate all parameter settings following the current configuration.

_is_on_a_beat() bool

Decide if we are on a beat or not, given the current cumulative performance time.

Returns:

True if we are on a beat.

property _roll_duration: float
Returns:

the duration of a single note in a roll.

property _slide_duration: float
Returns:

the duration of a slide.

advance_contours() None

Retrieve the next value of each contour and store it for future use.

approach_from_above(note_number: int, tune: Tune) int

Return the midi note number to approach the given note from above. If no special approach rule is specified in the configuration file, it will return the next note in the scale of the tune’s key from the given note.

Parameters:
  • note_number – the note to approach.

  • tune – the reference tune.

Returns:

the note used the approach the given note from above.

approach_from_below(note_number: int, tune: Tune) int

Return the midi note number to approach the given note from below. If no special approach rule is specified in the configuration file, it will return the previous note in the scale of the tune’s key from the given note.

Parameters:
  • note_number – the note to approach.

  • tune – the reference tune.

Returns:

the note used the approach the given note from below.

can_generate_ornament() bool
Returns:

whether or not to generate an ornament given the current ornament contour.

check_midi_control() Callable[[], None]

Returns a function that associates a contour name (values) for every MIDI control number in the dictionary (keys) and updates the groover accordingly. The value of the contour will be the control value mapped in the interval [0, 1].

Returns:

a callback function that will check for the given values.

choose_ornament(message: Message) str

Evaluate the ornament specific rules and chooose how the note will be ornamented.

Parameters:

message – the midi message to ornament.

Returns:

the chosen ornament type.

property current_tempo: int

The current tempo given the value of the tempo contour. If the option use_old_tempo_warp is set to True the contour affects tempo in terms of percentage of the original one (e.g. 20% faster); otherwise in terms of a fixed amount of bpms (e.g. 10 bpms faster). If an external tempo has been set, the calculated tempo will be interpolated with it according to the user specified percentage.

Returns:

the current tempo

generate_ornament(message: Message, ornament_type: str) list[Message]

Generate the sequence of notes corresponding to the chosen ornament.

Parameters:
  • message – the midi message to ornament.

  • ornament_type – the type of ornament to generate.

Returns:

the list of midi events corresponding to the chosen ornament.

get_end_notes() list[Message]

Generate an end note for the tune based on its key. :return: the midi messages containing the end note

jump_to_pos(pos: int) None

Jump to the specified song position.

Parameters:

pos – the position to jump to.

next_event()

Return the current event in the tune. Returns none if no event is available.

perform(message: Message) list[Message]

‘Perform’ a single note event by affecting its timing, pitch, velocity and adding ornaments.

Parameters:

message – the midi message to perform.

Returns:

the list of midi messages corresponding to the input message’s performance.

reset_clock() None

Reset the MIDI clock to initial tempo.

reset_contours() None

Reset all contours so that the next call to next() will yield the first value of each contour.

set_clock() None

Register a MIDI clock message and calculate the requested tempo.

set_contour_value(contour_name: str, value: float) None

Set the value of a given contour to a given value until the update.

Parameters:
  • contour_name – the name of the contour.

  • value – the value to set the contour to.

Raises:

groover.UnknownContourError – if the contour name does not correspond to any of the Groover’s contours.

set_tempo(tempo: int) None

Set the new performance tempo.

Parameters:

tempo – the requested tempo in bpms.

property tempo: int
Returns:

the user-set tempo.

exception loeric.groover.UnknownContourError

Raised if trying to set a contour whose name does not correspond to any of the Groover’s contours.

__weakref__

list of weak references to the object

class loeric.contour.Contour

A class representing a note-wise intensity conotour.

__init__()

Initialize the class.

__len__()

The length of this contour.

__weakref__

list of weak references to the object

calculate(midi: Tune) None

Calculate the intensity contour for the given tune.

Parameters:

midi – the input tune.

jump(index: int) None

Jump to the specified index in the contour.

Parameters:

index – the index to jump to.

Raises:

contour.InvalidIndexError – if the index exceeds the length of the contour.

next() float

Return the next element (i.e. intensity value) of the intensity contour.

Raises:
Returns:

the next intensity value.

reset() None

Resets the contour iteration. The next call to next() will return the first element of the contour.

scale_and_savgol(array: ndarray, savgol: bool = True, shift: bool = False, scale=False) ndarray

Scale the contour to have it range between 0 and 1. Optionally, apply a Savitzky-Golay filter with a window of 15 and order 3. Optionally, rescale the array to bring the extremes to 0 and 1. Optionally, shift the array to bring its mean closer to 0.5.

Parameters:
  • array – the input contour.

  • savgol – whether or not to apply the savgol filter.

  • scale – whether or not to rescale the array to use the full range.

  • shift – whether or not to shift the filtered array so that its mean is close to 0.5.

Returns:

the processed array.

class loeric.contour.HarmonicContour

A contour holding harmonic information.

__init__()

Initialize the class.

calculate(midi: Tune, chord_score: array, chords_per_bar: int = 2, allowed_chords: array = array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]), transpose: int = 0) None

Calculate the intensity contour for the given tune.

Parameters:

midi – the input tune.

class loeric.contour.IntensityContour

A contour given by the weighted sum of O’Canainn components.

__init__()

Initialize the class.

calculate(midi: Tune, weights: array = None, random_weight: float = 0, savgol: bool = True, shift: bool = False, scale: bool = False) None

Compute the contour as the weighted sum of O’Canainn component. An optional random component can be added.

Parameters:
  • midi – the input tune.

  • weights – the weights for the components, respectively frequency score, beat score, ambitus score, leap score and length score.

  • random_weight – the weight of the random component over the sum of the weighted O’Canainn scores. If None, the components will be averaged together.

  • savgol – whether or not to apply a final savgol filtering step (recommended).

  • shift – whether or not to apply a final shifting step to bring the mean of the array close to 0.5.

ocanainn_scores(midi: Tune) tuple[array, array, array, array, array]

Computes the individual components for the ocanainn score:

  • frequency score;

  • beat score;

  • ambitus score;

  • leap score;

  • length score.

Parameters:

midi – the input tune used to compute the individual scores.

Returns:

the frequency score, the beat score, the ambitus score, the leap score and the length score.

exception loeric.contour.InvalidIndexError

Raised if the index of the current value is below 0 or exceeds the length of the contour.

__weakref__

list of weak references to the object

class loeric.contour.MessageLengthContour

A contour holding the length of each note in the tune.

__init__()

Initialize the class.

calculate(midi: Tune) None

Calculate the contour as the length of each note message (from each note on message to the next note off message).

Parameters:

midi – the input tune object.

class loeric.contour.PatternContour

A contour made of a repeating pattern.

__init__()

Initialize the class.

calculate(midi: Tune, mean: array = array([1]), std: array = array([0]), std_scale: float = 1, normalize: bool = False, period: float = 0.5) None

Create the contour by repeating the input weights over the specified period. If standard deviatons are specified, the resulting patter is sampled from each distribution at each loaction.

Parameters:
  • mean – the pattern to repeat.

  • std – the std of the pattern to repeat, for every item.

  • period – the length of the pattern, in bars.

class loeric.contour.PhraseContour

A contour representing phrase arcs.

__init__()

Initialize the class.

calculate(midi: Tune) None

Compute a phrasing contour using a sum of sine functions.

Parameters:

midi – the input tune.

class loeric.contour.PitchContour

A contour holding the pitch of notes in the tune.

__init__()

Initialize the class.

calculate(midi: Tune, savgol: bool = True, shift: bool = True) None

Calculate the intensity contour for the given tune.

Parameters:

midi – the input tune.

class loeric.contour.PitchDifferenceContour

A contour holding the pitch difference between notes in the tune.

__init__()

Initialize the class.

calculate(midi: Tune) None

Calculate the intensity contour for the given tune.

Parameters:

midi – the input tune.

class loeric.contour.RandomContour

A randomly initialized contour.

__init__()

Initialize the class.

calculate(midi: Tune, extremes: tuple[float, float] = None) None

Compute a random contour following a uniform distribution in the specified range, by default between 0 and 1.

Parameters:
  • midi – the input tune.

  • extremes – the upper and lower bound for the random contour. If None, the range will be (0, 1).

exception loeric.contour.UncomputedContourError

Raised if the contour has not been computed yet.

__weakref__

list of weak references to the object

loeric.contour.multiply(contours: list[Contour])

Returns a new contour that holds the product of the input contours.

Parameters:

contours – the contours to multiply.

Returns:

a new contour holding the product of the input contours.

loeric.contour.weighted_sum(contours: list[Contour], weights: ndarray)

Returns a new contour that holds the weighted sum of the input contours.

Parameters:
  • contours – the contours to add.

  • weights – the weight for each contour.

Returns:

a new contour holding the weighted sum of the input contours.

class loeric.tune.Tune(filename: str, repeats: int)

A wrapper for a midi file.

__getitem__(idx: int) Message

Return the item in the midi event list corresponding to the given index.

Parameters:

idx – the element index.

Returns:

the midi message corresponding to that index.

__init__(filename: str, repeats: int)

Initialize the class. A number of properties is computed:

  • the duration of the pickup bar, if there is any;

  • the key signature (only the first encountered is considered, key signature changes are not supported);

  • the time signature (only the first encountered is considered, time signature changes are not supported);

Parameters:
  • filename – the path to the midi file.

  • repeats – how many times the tune should be repeated.

__len__() int

Return the length of the list of midi messages.

Returns:

the number of midi messages in this tune.

__weakref__

list of weak references to the object

_get_key_signature() str

Retrieve the key signature of the tune, if there is any. Only the first key signature will be retrieved.

Returns:

the first key signature if there is any, else None.

_get_original_tempo() int

Retrieve the tempo of the tune, if there is any. Only the first tempo change will be retrieved.

Returns:

the first tempo change if there is any, else None.

_get_performance_offset() float

Return the length of the pickup bar, if there is any.

Returns:

the length of the pickup bar in seconds.

_get_time_signature() TimeSignature

Retrieve the time signature of the tune, if there is any. Only the first time signature will be retrieved.

Returns:

the first time signature if there is any, else None.

property _quarter_duration: float

Return the duration of a quarter note in seconds given the current tempo.

Returns:

the amount of seconds corresponding to a quarter note given the current tempo.

property ambitus: tuple[int]
Returns:

the tune’s lowest and highest pitches in a tuple (low, high)

property bar_duration: float
Returns:

the tune’s bar duration in seconds.

property beat_count: int
Returns:

the numebr of beats in the tune.

property beat_duration: float
Returns:

the tune’s beat duration in seconds.

events() Generator[Message, None, None]

A generator returning each midi event in the tune. Each time an event is retrieved, the performance time is updated.

Returns:

the sequence of midi events one by one

filter(filtering_function: Callable[[Message], bool]) list[Message]

Retrieve the midi events that fullfill the given filtering function.

Parameters:

filtering_function – the function filtering the midi events.

Returns:

a list of midi events fullfilling the filtering function.

get_performance_time() float
Returns:

the current performance time

is_on_a_beat() bool

Decide if we are on a beat or not, given the current cumulative performance time.

Returns:

True if we are on a beat.

property key_signature: str
Returns:

the tune’s key signature.

property offset: float
Returns:

the tune’s performance offset (i.e. the length of the pickup bar) in seconds.

property quarter_duration: float
Returns:

the duration of a quarter note in the tune given its original tempo.

reset_performance_time() None
Returns:

the current performance time

property root: int
Returns:

the tune’s key signature root in pitch space.

semitones_from_tonic(midi_note: int) int

Compute the distance between the given note and the tonic of the tune in semitones.

Parameters:

midi_note – the input note.

Returns:

the distance between note and the tonic in semitones.

property tempo: int
Returns:

the tune’s tempo in microseconds per quarter.

property time_signature: TimeSignature
Returns:

the tune’s time signature.

class loeric.player.Player(tempo: int, key_signature: str, time_signature: TimeSignature, save: bool, midi_out, verbose: bool = False)

The class responsible for performance playback and saving.

__init__(tempo: int, key_signature: str, time_signature: TimeSignature, save: bool, midi_out, verbose: bool = False)

Initialize the class.

Parameters:
  • tempo – the performance’s tempo in microseconds per quarter note.

  • key_signature – the performance’s key signature.

  • time_signature – the performance’s time signature.

  • save – whether or not to save the performance to a midi file

  • midi_out – the output midi port.

__weakref__

list of weak references to the object

init_playback() None

Initialize variables useful to keep track of playback time and avoid drifting.

play(messages: list[Message]) None

Play the messages in input and append them to the generated performance. If no midi port has been specified, the messages only be saved.

Parameters:

messages – the midi messages to play.

reset() None

Reset the output port.

save(filename: str) None

Save the generated performance as a midi file.

Parameters:

filename – the path to the output midi file.