Envelope is a small utility class for working with envelopes: a curve that maps an x-position (time / index / “progress”) to a y-value (amplitude / parameter value / control signal). The class supports two common ways to define an envelope.
Construction
Sampled values (a list of y-values), you provide only y-values:
env1 = Envelope([0, 2, 1, 3, 2, 5, 2, 3, 0, 1, 0])
This is interpreted as a sequence of samples. Internally, it is converted to breakpoint pairs by adding x-indices:(0,0), (1,2), (2,1), ..., (10,0)
So for sampled envelopes, the “natural” (non-normalized) x-domain is:x in [0, n-1] where n = number of samples
Breakpoint pairs (a list of (x,y) points), you provide explicit x,y points:
env2 = Envelope([(0,5), (1,1), (5,1), (4,2)])
If autosort=True (default), points are sorted by x automatically before use. This makes it easier to enter points in any order without breaking the envelope. The core idea of envelope is the internal normalization. Regardless of how an envelope is built, it is stored internally in a normalized form:x_normalized in [0,1]
y_normalized in [0,1]
The original domains are remembered inself.x_range = (x_min, x_max)
self.y_range = (y_min, y_max)
This allows lookup in original units (e.g., index 0..10 or “time seconds” 0..5) or lookup in normalized units (0..1).
Methods
.getValue(xPos,...) evaluates the envelope at a given x-position
Parameters:
xPos
The position where you want the envelope value.normalizedX
True:xPosis already normalized to[0,1].
False(default):xPosis in the envelope’s original x-domain.- for
from_values, this means “sample index space” (0..n-1) - for
from_pairs, this means whatever x-values you provided
- for
-
normalizedY
True: return normalized y in[0,1]False(default): return the value in original y-units interpolateOutsideOfRangeTrue(default): the envelope extrapolates linearly beyond the first/last point.False: clamps to the first/last y-value.
Return: yValue(float)
.display(use_normalized=False, show_polynomials=False) plots the envelope with matplotlib.
Parameters:
use_normalizedFalse(default): Plotxandyin original units, based onx_range/y_rangeTrue: Plot the normalized envelope,xandyboth in[0,1]show_polynomialsTrue: additionally plots polynomial approximations (degrees 1..5) that were automatically fitted at initialization.
Return: None
EXAMPLE
from GreasyPidgin.Envelope import Envelopeenv1 = Envelope([0,2,1,3,2,5,2,3,0,1,0])env2 = Envelope([(0,5),(1,1),(5,1),(4,2)])# -------------------------------------------------------------# 1) Display envelopes# -------------------------------------------------------------# original units (index on x, original y)env1.display()# original units (your x-values, your y-values)env2.display()# normalized plot (x and y in [0,1])env2.display(True)# original plot + polynomial fitsenv2.display(False, True)# -------------------------------------------------------------# 2) Get values with and without normalization (env2)# -------------------------------------------------------------# Explanation:# - env2 has x-values around 0..5 (because you wrote x=0,1,5,4).# - normalizedX=True means xPos is 0..1.# - normalizedY=True means output is 0..1.# x=0.5 in normalized space, y normalizedprint(env2.getValue(0.5, True, True))# x=0.5 in original x units, y originalprint(env2.getValue(0.5, False, False))# see normalized shapeenv2.display(True) # see original unitsenv2.display()# ------------------------------------------------------------# env1 was built from values (samples), so its original x-domain is index space:# x in [0, 10] (because 11 values -> indices 0..10)# Here normalizedX=True:# xPos=0.1 means “10% through the envelope” in normalized [0,1] space.print(env1.getValue(0.1, True))# Here normalizedX=False:# xPos=0.1 means “index 0.1” (very close to the first sample),# which is different from “10% through the whole envelope”.print(env1.getValue(0.1, False))