scamp_extensions.utilities.time_varying_parameter.TimeVaryingParameter

class scamp_extensions.utilities.time_varying_parameter.TimeVaryingParameter(levels: Sequence = (0,), durations: Sequence[float] = (), curve_shapes: Sequence[float | str] = None, offset: float = 0, clock: Clock = None, units: str = 'beats')[source]

Bases: Envelope

A simple wrapper around Envelope that is aware of the current time or beat in the current clock. Simply call it like a function to get its value at the current time or beat.

Parameters:
  • levels – see Envelope

  • durations – see Envelope

  • curve_shapes – see Envelope

  • offset – see Envelope

  • clock – the clock whose time/beat this TimeVaryingParameter uses for lookup. Defaults to the current active clock.

  • units – either “beats” or “time”; whether or not to use the time or the beat of the clock to look up the parameter value

Methods

adsr(attack_length, attack_level, ...[, ...])

Same as adsr(), but taking an optional clock and units parameter.

ar(attack_length, release_length[, ...])

Same as ar(), but taking an optional clock and units parameter.

asr(attack_length, sustain_level, ...[, ...])

Same as asr(), but taking an optional clock and units parameter.

finished()

Whether this parameter has run its course, i.e. whether enough time has passed on its clock, since it was created, to reach the end of the underlying envelope.

from_function(function[, domain_start, ...])

Same as from_function(), but taking an optional clock and units parameter.

from_levels(levels[, length, offset, clock, ...])

Same as from_levels(), but taking an optional clock and units parameter.

from_levels_and_durations(levels, durations)

Same as from_levels_and_durations(), but taking an optional clock and units parameter.

from_list(constructor_list[, clock, units])

Same as from_list(), but taking an optional clock and units parameter.

from_points(*points[, clock, units])

Same as from_points(), but taking an optional clock and units parameter.

from_segments(segments[, clock, units])

Same as from_segments(), but taking an optional clock and units parameter.

release(duration[, start_level, ...])

Same as release(), but taking an optional clock and units parameter.

Inherited Methods

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.

average_level([t_range])

Returns the average value that the Envelope takes over the given range.

duplicate()

Returns a copy of this object by serializing to and from JSON.

end_level()

Ending value of the Envelope

end_time()

End of the domain on which this Envelope is defined.

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

json_dumps()

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.

max_absolute_slope()

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.

pop_segment()

Remove and return the last segment of this Envelope.

pop_segment_from_start()

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.

remove_segments_after(t)

Removes all segments after the given time (including a partial segment if t lands in the middle of a segment).

remove_segments_before(t)

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.

show_plot([title, resolution, ...])

Shows a plot of this Envelope using matplotlib.

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

start_level()

Beginning value of the Envelope

start_time()

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

curve_shapes

Tuple of all the segment curve shapes.

durations

Tuple of all the segment lengths.

levels

Tuple of levels at all segment boundary points.

offset

Alias for Envelope.start_time().

times

Tuple of all the segment start times.

finished()[source]

Whether this parameter has run its course, i.e. whether enough time has passed on its clock, since it was created, to reach the end of the underlying envelope.

classmethod from_segments(segments: Sequence[EnvelopeSegment], clock: Clock = None, units: str = 'beats') → T[source]

Same as from_segments(), but taking an optional clock and units parameter. (See TimeVaryingParameter)

classmethod from_levels_and_durations(levels: Sequence, durations: Sequence[float], curve_shapes: Sequence[float | str] = None, offset: float = 0, clock: Clock = None, units: str = 'beats') → T[source]

Same as from_levels_and_durations(), but taking an optional clock and units parameter. (See TimeVaryingParameter)

classmethod from_levels(levels: Sequence, length: float = 1.0, offset: float = 0, clock: Clock = None, units: str = 'beats') → T[source]

Same as from_levels(), but taking an optional clock and units parameter. (See TimeVaryingParameter)

classmethod from_list(constructor_list: Sequence, clock: Clock = None, units: str = 'beats') → T[source]

Same as from_list(), but taking an optional clock and units parameter. (See TimeVaryingParameter)

classmethod from_points(*points: Sequence, clock: Clock = None, units: str = 'beats') → T[source]

Same as from_points(), but taking an optional clock and units parameter. (See TimeVaryingParameter)

classmethod release(duration: float, start_level=1, curve_shape: float | str = None, clock: Clock = None, units: str = 'beats') → T[source]

Same as release(), but taking an optional clock and units parameter. (See TimeVaryingParameter)

classmethod ar(attack_length: float, release_length: float, peak_level=1, attack_shape: float | str = None, release_shape: float | str = None, clock: Clock = None, units: str = 'beats') → T[source]

Same as ar(), but taking an optional clock and units parameter. (See TimeVaryingParameter)

classmethod asr(attack_length: float, sustain_level, sustain_length: float, release_length: float, attack_shape: float | str = None, release_shape: float | str = None, clock: Clock = None, units: str = 'beats') → T[source]

Same as asr(), but taking an optional clock and units parameter. (See TimeVaryingParameter)

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, clock: Clock = None, units: str = 'beats') → T[source]

Same as adsr(), but taking an optional clock and units parameter. (See TimeVaryingParameter)

classmethod from_function(function: Callable[[float], float], domain_start: float = 0, domain_end: float = 1, resolution_multiple: int = 2, key_point_precision: int = 2000, key_point_iterations: int = 5, clock: Clock = None, units: str = 'beats') → T[source]

Same as from_function(), but taking an optional clock and units parameter. (See TimeVaryingParameter)

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.

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.

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. Pass rounded=True to 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 within max_error for 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_width away from the endpoints of the segment it falls in. If t lands 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 t may come to an existing control point before it is treated as coincident with it, expressed as a fraction of the width of the segment containing t.

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 t is 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.

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

show_plot(title: str = None, resolution: int = 25, show_segment_divisions: bool = True, x_range: tuple[float, float] = None, y_range: tuple[float, float] = None) → None

Shows a plot of this Envelope using matplotlib.

Parameters:
  • title – A title to give the plot.

  • resolution – number of points to use per envelope segment

  • show_segment_divisions – Whether or not to place dots at the division points between envelope segments

  • x_range – min and max value shown on the x-axis

  • y_range – min and max value shown on the y-axis

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.