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_velocity: int
- Returns:
the current velocity given the value of the velocity contour.
- _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.
- 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_contours() None
Reset all contours so that the next call to next() will yield the first value of each contour.
- 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.
- 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:
contour.UncomputedContourError – if the contour has not been computed yet.
contour.InvalidIndexError – if the index of the current value is below 0 or exceeds the length of the contour.
- 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.
- 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.
- 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.
- class loeric.contour.PitchContour
A contour holding the pitch of notes in the tune.
- __init__()
Initialize the class.
- class loeric.contour.PitchDifferenceContour
A contour holding the pitch difference between notes in the tune.
- __init__()
Initialize the class.
- 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.
- 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.
- 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 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.
- 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 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.