clockblocks.tempo_envelope.TempoEnvelope
- class clockblocks.tempo_envelope.TempoEnvelope(levels: float | Sequence[float] = (60,), durations: Sequence[float] = (), curve_shapes: Sequence[float | str] = None, units: str = 'tempo', duration_units: str = 'beats')[source]
Bases:
EnvelopeA subclass of
Envelopethat is specifically designed for representing changing tempo curves. The underlying envelope represents beat length as a function of the current beat, which means that the area under the curve represents how much time should pass from one beat to the next (beats * sec/beat = sec). Although the methods take a “units” argument, which can be “beatlength”, “tempo”, or “rate”, these are always converted to beat length in the underlying representation- Parameters:
levels – levels of the curve segments (i.e. tempo values) in the units specified by the units argument
durations – durations of the curve segments in the units specified by the duration_units argument
curve_shapes – see
from_levels_and_durations()units – one of “tempo”, “rate” or “beat length”, determining how we interpret the levels given
duration_units – either “beats” or “time”, determining how we interpret the durations given
Methods
beat_at_time(t)Determine the beat at the given time, measured from time 0.
beat_length_at(beat[, from_left])Get the beat length at the given beat.
Warps this tempo_curve so that all the locations of key points get re-interpreted as times instead of beat locations.
convert_units(values, input_units, output_units)Utility method to convert values between unites of tempo, rate and beat length.
extend_to(beat)Extends the end of this TempoEnvelope to the given beat (if needed) by adding a constant segment at the end.
from_function(function[, domain_start, ...])Constructs a TempoEnvelope that approximates an arbitrary function.
from_levels(levels[, length, units, ...])Constructs a TempoEnvelope from the given levels and total length, using the specified units.
from_levels_and_durations([levels, ...])Constructs a TempoEnvelope from the given levels, durations and curve shapes, using the specified units.
from_list(constructor_list[, units, ...])Construct a TempoEnvelope from a list that can take a number of formats
from_points(*points[, units, duration_units])Construct an envelope from a list of (beat/time, tempo/rate/beat length) pairs.
True if this is the trivial default tempo — a constant 60 bpm (rate 1.0) with no duration, i.e. structurally identical to a freshly-constructed
TempoEnvelope().rate_at(beat[, from_left])Get the beat rate (in beats/second) at the given beat.
show_plot([title, resolution, ...])Shows a plot of this TempoEnvelope using matplotlib.
tempo_at(beat[, from_left])Get the tempo (in beats/minute) at the given beat.
time_at_beat(beat)Determine the time at the given beat by integrating the beat-length curve from beat 0 (seconds/beat * beats = seconds).
truncate_at(beat)Removes all segments after the given beat and adds a constant segment if necessary to bring us up to that beat.
Inherited Methods
adsr(attack_length, attack_level, ...[, ...])Construct a standard attack/decay/sustain/release envelope
append_envelope(envelope_to_append)Extends this envelope by another one (shifted to start at the end of this one).
append_segment(level, duration[, ...])Append a segment to the end of the curve ending at level and lasting for duration.
ar(attack_length, release_length[, ...])Construct an attack/release envelope
asr(attack_length, sustain_level, ...[, ...])Construct an attack/sustain/release envelope
average_level([t_range])Returns the average value that the Envelope takes over the given range.
Returns a copy of this object by serializing to and from JSON.
Ending value of the Envelope
end_time()End of the domain on which this Envelope is defined.
from_segments(segments)Create a new envelope from a list of
EnvelopeSegments.get_durations([rounded])Tuple of all the segment lengths.
get_upper_integration_bound(t1, desired_area)Given a lower integration bound, find the upper bound that will result in the desired integral.
insert(t, level[, curve_shape_in, ...])Insert a curve point at time t, and set the shape of the curve into and out of it.
insert_interpolated(t[, relative_tolerance])Insert another curve point at the given time, without changing the shape of the curve.
integrate_interval(t1, t2)Get the definite integral under this Envelope from t1 to t2
is_shifted_version_of(other[, tolerance])Determines if this segment is simply a shifted version of another segment
Dump this object as a JSON string.
json_loads(s)Load this object from a JSON string.
length()The length of the domain on which this Envelope is defined (end time minus start time).
load_from_json(file_path)Load this object from a JSON file with the given path.
local_extrema([include_saddle_points])Returns a list of the times where the curve changes direction.
Returns the maximum absolute value of the slope over the entire Envelope.
max_level([t_range])Returns the highest value that the Envelope takes over the given range.
min_level([t_range])Returns the lowest value that the Envelope takes over the given range.
normalize_to_duration(desired_duration[, ...])Stretch or squeeze the segments of this Envelope so that it has the desired total duration.
Remove and return the last segment of this Envelope.
Remove and return the first segment of this Envelope.
prepend_envelope(envelope_to_prepend)Extends this envelope backwards by another one (shifted to end at the start of this one).
prepend_segment(level, duration[, ...])Prepend a segment to the beginning of the curve, starting at level and lasting for duration.
release(duration[, start_level, curve_shape])Construct an simple decaying envelope
Removes all segments after the given time (including a partial segment if t lands in the middle of a segment).
Removes all segments before the given time (including a partial segment if t lands in the middle of a segment).
save_to_json(file_path)Save this object to a JSON file using the given path.
scale_horizontal(amount)Scales the domain of this Envelope by the specified amount.
scale_vertical(amount)Scales the levels of this segment by the specified amount.
shift_horizontal(amount)Shifts the domain of this Envelope by the specified amount.
shift_vertical(amount)Shifts the levels of this Envelope the specified amount.
simplified([use_curvature, ...])Simplifies the envelope to only extrema and inflection points.
split_at(t[, change_original, zero_out_offsets])Splits the Envelope at one or several points and returns a tuple of the pieces
Beginning value of the Envelope
Beginning of the domain on which this Envelope is defined.
value_at(t[, from_left])Get the value of this Envelope at the given time.
Attributes
Tuple of all the segment curve shapes.
Tuple of all the segment lengths.
Tuple of levels at all segment boundary points.
Alias for
Envelope.start_time().Tuple of all the segment start times.
- classmethod from_levels_and_durations(levels: Sequence = (0, 0), durations: Sequence[float] = (0,), curve_shapes: Sequence[float | str] = None, units: str = 'tempo', duration_units: str = 'beats') TempoEnvelope[source]
Constructs a TempoEnvelope from the given levels, durations and curve shapes, using the specified units.
- Parameters:
levels – levels of the curve segments (i.e. tempo values) in the units specified by the units argument
durations – durations of the curve segments in the units specified by the duration_units argument
curve_shapes – see
from_levels_and_durations()units – one of “tempo”, “rate” or “beat length”, determining how we interpret the levels given
duration_units – either “beats” or “time”, determining how we interpret the durations given
- Returns:
a TempoEnvelope, constructed accordingly
- classmethod from_levels(levels: Sequence[float], length: float = 1.0, units: str = 'tempo', duration_units: str = 'beats') TempoEnvelope[source]
Constructs a TempoEnvelope from the given levels and total length, using the specified units.
- Parameters:
levels – levels of the curve segments (i.e. tempo values) in the units specified by the units argument
length – total length of the tempo curve, in the units specified by the duration_units argument
units – one of “tempo”, “rate” or “beat length”, determining how we interpret the levels given
duration_units – either “beats” or “time”, determining how we interpret the durations given
- Returns:
a TempoEnvelope, constructed accordingly
- classmethod from_list(constructor_list: Sequence, units: str = 'tempo', duration_units: str = 'beats') TempoEnvelope[source]
Construct a TempoEnvelope from a list that can take a number of formats
- Parameters:
constructor_list – see
from_list()units – one of “tempo”, “rate” or “beat length”, determining how we interpret the levels given
duration_units – either “beats” or “time”, determining how we interpret the durations given
- Returns:
a TempoEnvelope, constructed accordingly
- classmethod from_points(*points, units: str = 'tempo', duration_units: str = 'beats') TempoEnvelope[source]
Construct an envelope from a list of (beat/time, tempo/rate/beat length) pairs. Units are defined by the units and duration_units parameters.
- Parameters:
points – list of points, each of which is of the form (time, value) or (time, value, curve_shape)
units – one of “tempo”, “rate” or “beat length”, determining how we interpret the tempo values
duration_units – either “beats” or “time”, determining how we interpret the time values
- Returns:
a TempoEnvelope, constructed accordingly
- classmethod from_function(function, domain_start=0, domain_end=1, units: str = 'tempo', duration_units: str = 'beats', scanning_step_size: float = 0.05, key_point_resolution_multiple: int = 2, iterations: int = 6, min_key_point_distance: float = 1e-07) TempoEnvelope[source]
Constructs a TempoEnvelope that approximates an arbitrary function. The domain of the function is in units defined by the duration_units parameter, and the range is in units defined by the units parameter.
- Parameters:
function – A function from beat/time to tempo/rate/beat length, as defined by the duration_units and units parameters.
domain_start – see
from_function()domain_end – see
from_function()units – one of “tempo”, “rate” or “beat length”, determining how we interpret the function output
duration_units – either “beats” or “time”, determining how we interpret the function input
scanning_step_size – when analyzing the function for discontinuities, maxima and minima, inflection points, etc., use this step size for the initial pass.
key_point_resolution_multiple – factor by which we add extra key points between the extrema and inflection points to improve the curve fit.
iterations – when a potential key point is found, we zoom in and scan again in the viscinity of the point. This determines how many iterations of zooming we do.
min_key_point_distance – after scanning for key points, any that are closer than this distance are merged.
- Returns:
a TempoEnvelope, constructed accordingly
- beat_length_at(beat: float, from_left: bool = False) float[source]
Get the beat length at the given beat. If the beat length jumps at the given beat, the default is to return the beat length after the jump, though this can be overridden with the from_left argument.
- Parameters:
beat – the beat at which to get the beat length
from_left – whether to evaluate from the right or left-hand side of the beat in question
- rate_at(beat: float, from_left: bool = False) float[source]
Get the beat rate (in beats/second) at the given beat. If the rate jumps at the given beat, the default is to return the rate after the jump, though this can be overridden with the from_left argument.
- Parameters:
beat – the beat at which to get the rate
from_left – whether to evaluate from the right or left-hand side of the beat in question
- tempo_at(beat: float, from_left: bool = False) float[source]
Get the tempo (in beats/minute) at the given beat. If the tempo jumps at the given beat, the default is to return the tempo after the jump, though this can be overridden with the from_left argument.
- Parameters:
beat – the beat at which to get the tempo
from_left – whether to evaluate from the right or left-hand side of the beat in question
- extend_to(beat: float) TempoEnvelope[source]
Extends the end of this TempoEnvelope to the given beat (if needed) by adding a constant segment at the end.
- truncate_at(beat: float) TempoEnvelope[source]
Removes all segments after the given beat and adds a constant segment if necessary to bring us up to that beat.
- Parameters:
beat – the beat that we are truncating the tempo envelope after
- Returns:
self, for chaining purposes
- static convert_units(values: float | Sequence[float], input_units: str, output_units: str) float | Sequence[float][source]
Utility method to convert values between unites of tempo, rate and beat length.
- Parameters:
values – value or list of values in terms of the input_units
input_units – current units of the given values (either “tempo”, “rate”, or “beat length”)
output_units – desired units to convert to (either “tempo”, “rate”, or “beat length”)
- Returns:
the list of values, converted to output units
- convert_durations_to_times()[source]
Warps this tempo_curve so that all the locations of key points get re-interpreted as times instead of beat locations. For instance, a tempo curve where the rate hovers around 2 will see a segment of length 3 get stretched into a segment of length 6, since if it’s supposed to take 3 seconds, it would take 6 beats. Pretty confusing, but when we want to construct a tempo curve specifying the times that changes occur rather than the beats, we can first construct it as though the durations were in beats, then call this function to warp it so that the durations are in time.
- Returns:
self, altered accordingly
- show_plot(title=None, resolution=25, show_segment_divisions=True, units='tempo', x_range=None, y_range=None)[source]
Shows a plot of this TempoEnvelope using matplotlib.
- Parameters:
title – A title to give the plot.
resolution – number of points to use per envelope segment
show_segment_divisions – Whether to place dots at the division points between envelope segments
units – one of “tempo”, “rate” or “beat length”, determining the units of the y-axis
x_range – min and max value shown on the x-axis
y_range – min and max value shown on the y-axis
- is_default() bool[source]
True if this is the trivial default tempo — a constant 60 bpm (rate 1.0) with no duration, i.e. structurally identical to a freshly-constructed
TempoEnvelope(). (Note this is stricter than “functionally constant at 60”: a flat-60 envelope that carries a real duration or extra segments is not default (and worth surfacing for debugging)
- time_at_beat(beat: float) float[source]
Determine the time at the given beat by integrating the beat-length curve from beat 0 (seconds/beat * beats = seconds).
- Parameters:
beat – the beat at which to calculate the time
- beat_at_time(t: float) float[source]
Determine the beat at the given time, measured from time 0. This is the inverse of
time_at_beat().- Parameters:
t – the time at which to calculate the beat
- classmethod adsr(attack_length: float, attack_level, decay_length: float, sustain_level, sustain_length: float, release_length: float, attack_shape: float | str = None, decay_shape: float | str = None, release_shape: float | str = None) T
Construct a standard attack/decay/sustain/release envelope
- Parameters:
attack_length – rise time
attack_level – level reached after attack before decay
decay_length – length of decay portion of the curve
sustain_level – sustain level reached after decay and before release
sustain_length – length of sustain portion of curve
release_length – release time
attack_shape – sets curve shape for attack portion of the curve (see documentation for
Envelope.from_levels_and_durations())decay_shape – sets curve shape for decay portion of the curve (see documentation for
Envelope.from_levels_and_durations())release_shape – sets curve shape for release portion of the curve (see documentation for
Envelope.from_levels_and_durations())
- Returns:
an Envelope constructed accordingly
- append_envelope(envelope_to_append: T) T
Extends this envelope by another one (shifted to start at the end of this one).
- append_segment(level, duration: float, curve_shape: float = None, tolerance: float = 0, halfway_level=None) None
Append a segment to the end of the curve ending at level and lasting for duration. If we’re adding a linear segment to a linear segment, then we extend the last linear segment instead of adding a new one if the level is within tolerance of where the last one was headed
- Parameters:
level – the level we’re going to
duration – the duration of the new segment
curve_shape – defaults to 0 (linear)
tolerance – tolerance for extending a linear segment rather than adding a new one
halfway_level – alternate way of defining the curve shape. If this is set and the curve shape is not then we use this to determine the curve shape.
- classmethod ar(attack_length: float, release_length: float, peak_level=1, attack_shape: float | str = None, release_shape: float | str = None) T
Construct an attack/release envelope
- Parameters:
attack_length – rise time
release_length – release time
peak_level – level reached after attack and before release (see documentation for
Envelope.from_levels_and_durations())attack_shape – sets curve shape for attack portion of the curve (see documentation for
Envelope.from_levels_and_durations())release_shape – sets curve shape for release portion of the curve (see documentation for
Envelope.from_levels_and_durations())
- Returns:
an Envelope constructed accordingly
- classmethod asr(attack_length: float, sustain_level, sustain_length: float, release_length: float, attack_shape: float | str = None, release_shape: float | str = None) T
Construct an attack/sustain/release envelope
- Parameters:
attack_length – rise time
sustain_level – sustain level reached after attack and before release
sustain_length – length of sustain portion of curve
release_length – release time
attack_shape – sets curve shape for attack portion of the curve (see documentation for
Envelope.from_levels_and_durations())release_shape – sets curve shape for release portion of the curve (see documentation for
Envelope.from_levels_and_durations())
- Returns:
an Envelope constructed accordingly
- average_level(t_range: tuple[float, float] = None)
Returns the average value that the Envelope takes over the given range.
- Parameters:
t_range – tuple defining the start and end time of the interval to check. If None, return the average level reached over the entire Envelope.
- property curve_shapes: Sequence[float | str]
Tuple of all the segment curve shapes.
- duplicate() T
Returns a copy of this object by serializing to and from JSON.
- property durations: Sequence[float]
Tuple of all the segment lengths.
- end_level()
Ending value of the Envelope
- end_time() float
End of the domain on which this Envelope is defined.
- classmethod from_segments(segments: Sequence[EnvelopeSegment]) T
Create a new envelope from a list of
EnvelopeSegments.- Parameters:
segments – list of segments
- get_durations(rounded: bool = False) Sequence[float]
Tuple of all the segment lengths.
Segment lengths are derived as
end_time - start_time, so an envelope built up on a regular grid (e.g. repeated 0.1-long segments) accumulates floating-point dust in these differences even though each segment was appended with a clean duration. Passrounded=Trueto snap each length to a nearby nice decimal — used for serialization / repr so the intended durations show, without disturbing the exact segment boundary times that the curve math relies on.- Parameters:
rounded – if True, snap each duration to a nearby nice decimal (see
snap_float_to_nice_decimal())
- get_upper_integration_bound(t1: float, desired_area: float, max_error: float = 1e-14) float
Given a lower integration bound, find the upper bound that will result in the desired integral.
Walks the envelope segment by segment, subtracting each segment’s closed-form area until the remaining area falls within a single segment, then inverts that segment using
EnvelopeSegment.get_t_at_integral()— exactly for constant/linear segments, and to withinmax_errorfor exponential ones.Assumes a non-negative integrand, so that the integral increases monotonically (true for tempo and dynamics curves, the primary use). Regions before the first segment and after the last are treated as flat (at the start/end level), matching
integrate_interval().- Parameters:
t1 – lower bound of integration
desired_area – desired value of the integral.
max_error – convergence tolerance forwarded to the per-segment inverse for the exponential case; ignored by the (exact) constant and linear cases.
- insert(t, level, curve_shape_in=0, curve_shape_out=0) None
Insert a curve point at time t, and set the shape of the curve into and out of it. This essentially divides the segment at that point in two.
- Parameters:
t – The time at which to add a point
level – The level of the new point we are adding
curve_shape_in – the curve shape of the new segment going into the point we are adding
curve_shape_out – the curve shape of the new segment going out of the point we are adding
- insert_interpolated(t: float, relative_tolerance: float = 1e-07) float
Insert another curve point at the given time, without changing the shape of the curve. Returns the t value of the new (or already existing) interpolated point.
To avoid manufacturing degenerate, near-zero-width segments due to floating point imprecision, a point is only added if it is at least
relative_tolerance * segment_widthaway from the endpoints of the segment it falls in. Iftlands within that tolerance of an existing control point, this function call is assumed to be an idempotent re-insertion of an existing curve point after floating point error has muddied the waters. In this case no point is added, and the existing control point’s time is returned instead.- Parameters:
t – the point at which to insert the point
relative_tolerance – how close
tmay come to an existing control point before it is treated as coincident with it, expressed as a fraction of the width of the segment containingt.
- Returns:
the t value at which we interpolated. If we snapped to an existing control point, its t is returned; otherwise the passed value of
tis returned.
- integrate_interval(t1: float, t2: float)
Get the definite integral under this Envelope from t1 to t2
- Parameters:
t1 – lower bound of integration
t2 – upper bound of integration
- is_shifted_version_of(other: T, tolerance: float = 1e-10) bool
Determines if this segment is simply a shifted version of another segment
- Parameters:
other – another EnvelopeSegment
tolerance – how close it needs to be to count as the same
- json_dumps() str
Dump this object as a JSON string. This uses a custom encoder that recognizes and appropriately converts any attributes that are object inheriting from SavesToJSON.
- classmethod json_loads(s: str) T
Load this object from a JSON string. This uses a custom decoder that looks for a “_type” key in any object/dictionary being parsed and converts it to the class specified (assuming it a subclass of SavesToJSON).
- Parameters:
s – a string representing this object in JSON format
- length() float
The length of the domain on which this Envelope is defined (end time minus start time).
- property levels: Sequence
Tuple of levels at all segment boundary points.
- classmethod load_from_json(file_path: str) T
Load this object from a JSON file with the given path. This uses a custom decoder that looks for a “_type” key in any object/dictionary being parsed and converts it to the class specified (assuming it a subclass of SavesToJSON).
- Parameters:
file_path – path for loading the file
- local_extrema(include_saddle_points: bool = False) Sequence[float]
Returns a list of the times where the curve changes direction.
- Parameters:
include_saddle_points – if True, also include points where the curve starts or stops plateauing
- max_absolute_slope()
Returns the maximum absolute value of the slope over the entire Envelope.
- max_level(t_range: tuple[float, float] = None)
Returns the highest value that the Envelope takes over the given range.
- Parameters:
t_range – tuple defining the start and end time of the interval to check. If None, return the max level reached over the entire Envelope.
- min_level(t_range: tuple[float, float] = None)
Returns the lowest value that the Envelope takes over the given range.
- Parameters:
t_range – tuple defining the start and end time of the interval to check. If None, return the max level reached over the entire Envelope.
- normalize_to_duration(desired_duration: float, in_place: bool = True) T
Stretch or squeeze the segments of this Envelope so that it has the desired total duration.
- Parameters:
desired_duration – the desired new duration of the Envelope
in_place – if True, modifies this Envelope in place; if False, makes a copy first
- property offset: float
Alias for
Envelope.start_time().
- pop_segment() EnvelopeSegment | None
Remove and return the last segment of this Envelope. If there is only one segment, reduce it to length zero and return None.
- pop_segment_from_start() EnvelopeSegment | None
Remove and return the first segment of this Envelope. If there is only one segment, reduce it to length zero and return None.
- prepend_envelope(envelope_to_prepend: T) T
Extends this envelope backwards by another one (shifted to end at the start of this one).
- prepend_segment(level, duration: float, curve_shape: float = None, tolerance: float = 0, halfway_level=None) None
Prepend a segment to the beginning of the curve, starting at level and lasting for duration. If we’re adding a linear segment to a linear segment, then we extend the last linear segment instead of adding a new one if the level is within tolerance of where the last one was headed
- Parameters:
level – the level that the prepended segment starts at
duration – the duration of the new segment
curve_shape – defaults to 0 (linear)
tolerance – tolerance for extending a linear segment rather than adding a new one
halfway_level – alternate way of defining the curve shape. If this is set and the curve shape is not then we use this to determine the curve shape.
- classmethod release(duration: float, start_level=1, curve_shape: float | str = None) T
Construct an simple decaying envelope
- Parameters:
duration – total decay length
start_level – level decayed from
curve_shape – shape of the curve (see documentation for
Envelope.from_levels_and_durations())
- Returns:
an Envelope constructed accordingly
- remove_segments_after(t: float) None
Removes all segments after the given time (including a partial segment if t lands in the middle of a segment).
- Parameters:
t – the point at which this Envelope is to be truncated.
- remove_segments_before(t: float) None
Removes all segments before the given time (including a partial segment if t lands in the middle of a segment).
- Parameters:
t – the point at which this Envelope is to be truncated.
- save_to_json(file_path: str) None
Save this object to a JSON file using the given path. This uses a custom encoder that recognizes and appropriately converts any attributes that are object inheriting from SavesToJSON.
- Parameters:
file_path – path for saving the file
- scale_horizontal(amount: float) T
Scales the domain of this Envelope by the specified amount.
- Parameters:
amount – amount to scale domain by
- Returns:
self, for chaining purposes
- scale_vertical(amount) T
Scales the levels of this segment by the specified amount.
- Parameters:
amount – amount to scale output by
- Returns:
self, for chaining purposes
- shift_horizontal(amount: float) T
Shifts the domain of this Envelope by the specified amount.
- Parameters:
amount – the amount to shift the domain by
- Returns:
self, for chaining purposes
- shift_vertical(amount) T
Shifts the levels of this Envelope the specified amount.
- Parameters:
amount – the amount to shift up and down by
- Returns:
self, for chaining purposes
- simplified(use_curvature=True, include_saddle_points=True) T
Simplifies the envelope to only extrema and inflection points.
- Parameters:
use_curvature – if True, adjusts the curvature of the segments to match the half-way point between key points. If False, linearly interpolate between key points.
include_saddle_points – if True, include saddle points as well as extrema
- split_at(t: float | Sequence[float], change_original: bool = False, zero_out_offsets: bool = True) Sequence[T]
Splits the Envelope at one or several points and returns a tuple of the pieces
- Parameters:
t – either the time t or a tuple/list of times t at which to split the curve
change_original – if true, the original Envelope gets turned into the first of the returned tuple
zero_out_offsets – if true, each new piece is shifted to start at zero
- Returns:
tuple of Envelopes representing the pieces this has been split into
- start_level()
Beginning value of the Envelope
- start_time() float
Beginning of the domain on which this Envelope is defined.
- property times: Sequence[float]
Tuple of all the segment start times.
- value_at(t: float, from_left: bool = False)
Get the value of this Envelope at the given time.
- Parameters:
t – the time
from_left – if true, get the limit as we approach t from the left. In the case of a zero-length segment, which suddenly changes the value, this tells us what the value was right before the jump instead of right after the jump.