Envelope

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 in
self.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: xPos is already normalized to [0,1].
    False (default): xPos is 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
  • normalizedY
    True: return normalized y in [0,1]
    False (default): return the value in original y-units
  • interpolateOutsideOfRange
    True (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_normalized
    False (default): Plot x and y in original units, based on x_range / y_range
    True: Plot the normalized envelope, x and y both in [0,1]
  • show_polynomials
    True: additionally plots polynomial approximations (degrees 1..5) that were automatically fitted at initialization.

Return: None

EXAMPLE
Python
from GreasyPidgin.Envelope import Envelope
env1 = 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 fits
env2.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 normalized
print(env2.getValue(0.5, True, True))
# x=0.5 in original x units, y original
print(env2.getValue(0.5, False, False))
# see normalized shape
env2.display(True)
# see original units
env2.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))