++ed by:
Yegor Korablev

# NAME

``   Math::SegmentedEnvelope - create/manage/evaluate segmented (curved) envelope``

# SYNOPSIS

``````    my \$e = Math::SegmentedEnvelope->new; # creates random envelope with total duration = 1
\$e->segments; # number of segments
\$e->levels(map \$_ * 3, \$e->levels); # alter levels
\$e->durs(map \$_ * (rand(0.5) + 0.5), \$e->durs); # alter segments durations
\$e->curves(map \$_ + 1, \$e->curves); # alter curves
\$e->level( # alter start level of first segment and set it value to end level of last segment
\$self->segments,
\$e->level(0, \$self->level(0) - 1)
);
\$e->curve(0, 3); # set curvative for first segment
\$e->dur(0, 0.2); # set duration of first segment, so overall duration will changed
\$e->duration; # get it
\$e->normalize_duration; # scale segment durs so envelope duration will be 1``````

or

``````    my \$e = Math::SegmentedEnvelope->new( # creates custom adsr envelope
# def =>
[
[0,1,0.8,0.7,0], # levels
[0.1,0.2,0.4,0.3], # segment duration
[1,2,1,-3] # segment curvative, 0 for flat, 1 for linear and other for curved
],
is_morph => 1, # default is 0, smooth envelope with default value morpher inside segment
# morpher => sub { ... } # provide custom morpher which will recieve values from 0 to 1, and should return values from 0 to 1 for non-breaking envelope
is_hold => 0, # default is 0, will return boundary levels for positions out of envelope duration
is_fold_over => 1, # default is 0, will fold envelope instead of wraping for positions out of envelope duration
is_wrap_neg => 1, # default is 0, will wrap envelope insted of fold for negative positions
);
\$e->at(rand); # outputs value at random position
\$e->table(1024); # returns list of values(lookup table) for current envelope with specified size

my \$s = \$e->static; # creates static evaluator with current envelope parameters (coderef)
\$s->(rand); # outputs value at random position, but ~ 50% faster``````

# DESCRIPTION

This module gives abstraction of segmeneted envelope. You can create, modify and make static evaluators, which is faster than object method `at` calling.

# METHODS

## new(\$definiton, %params)

\$definition is a 3 element arrayref of arrayrefs - [[@levels], [@durations], [@curves]]

%params can provide additional params such `is_hold`,`is_morph`,`morpher`,`is_fold_over`,`is_wrap_neg`

if nothing specified, than random normalized envelope will be created, but you can provide `border_level` param to define levels at begin/end

## at(\$pos)

get value at specified position, position can be out of envelope duration, so it will hold or wrap or fold according configuration [arameters `is_hold`, `is_fold_over` and `is_wrap_neg`

## static

returns static evaluator (i.e. coderef) with current envelope object settings

## table(\$size)

returns lookup table (i.e. list of values) of specified size(default = 1024) which represents current form of the envelope

## is_morph(0|1)

get/set flag which turns on value morphing inside segment before applying curving and scaling. default = 0

## morpher(sub {...})

get/set morpher, default morpher is a `sub { sin(PI/2*\$_[0]) ** 2 }` which smoothes envelope, morpher recieves value from 0 to 1 and should also has same output range

## is_hold(0|1)

when this flag is turned on than `at` method and static evaluator with out-of-duration argument will return boundary levels. default = 0

## is_fold_over(0|1)

when this flag is turned on and `is_hold` is turned off than `at` argument greater than envelope duration will be folded around envelope duration insted of wraping. default = 0

## is_wrap_neg(0|1)

when this flag is turned on and `is_hold` is turned off than `at` argument < 0 will be wraped around envelope insted of folding. default = 0

## segments

returns number of segments

## duration

returns total envelope duration

## normalize_duration

scale segment durations, so total duration becomes 1

## def

returns current envelope definition (see `new` method)

## levels(@new_levels)

get/set envelope levels, if setting than provided list must have equal length to levels specified in initial definition (i.e. `segments + 1`)

## level(\$pos, [\$level])

get/set level at position

## durs([@new_durs])

same as `levels`, but get/sets durations for segments

## dur(\$pos, [\$level])

get/set dur at position

## curves([@new_curves])

same as durs, but for curves

## curve(\$pos, [\$level])

get/set curvative at position

## border_level([\$begin,\$end])

should be in range [0,1], can be array ref or single value for equal levels at begin/end of envelope

this will work only when passed to constructor to create random envelope, e.g.

``   Math::SegmentedEnvelope->new(border_level => [0.5,0.2])``

# FUNCTIONS

env(constructor params)

handy function which will construct object from passed params, e.g.

``````   use Math::SegmentedEnvelope 'env';
my @arr = map { env } 0..9; # @arr now contains 10 random envelope objects``````

alternatively you can use modules such as aliased, as

# EXAMPLES

Plot
``````   use PDL::Graphics::Prima::Simple;
use Math::SegmentedEnvelope;
my \$e = Math::SegmentedEnvelope->new(is_morph => 1);
my \$t = \$e->table(4096);
line_plot(\$t);
``````
Interpolate
``````   use PDL;
interpolate(rand(4095), sequence(4096), pdl(\$t)); # interpolate at random position ``````
Animation
``````   use AnyEvent; # and/or Coro
my \$e = Math::SegmentedEnvelope->new(is_morph => 1);
my \$v = 0; # some property
my \$w = AE::timer(0, 1/60, sub { # refresh \$v with 60Hz rate
state \$s = \$e->static; # get static evaluator
state \$started = AE::now;
\$v = \$s->(AE::now - \$started); # or \$e->at(..) if \$e can be altered somewhere
});
my \$k = AE::timer(10, 0, sub { undef \$w }); # kill previous timer after 10secs
my \$i = AE::idle(sub { ... }); # animate \$v using OpenGL, SDL and etc..
AE::cv->recv;``````
visual representation of arbitrary definition
``      Math::SegmentedEnvelope->new([[0,1,0.5,0],[0.5,0.5,1],[-3,1/3,4]], is_morph => 1)``

# AUTHOR

Yegor Korablev <egor@cpan.org>