=encoding utf8

=head1 NAME

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

=head1 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

=head1 DESCRIPTION

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

=head1 METHODS

=head2 new($definiton, %params)

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

%params can provide additional params such C<is_hold>,C<is_morph>,C<morpher>,C<is_fold_over>,C<is_wrap_neg>

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

=head2 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 C<is_hold>, C<is_fold_over> and C<is_wrap_neg>

=head2 static

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

=head2 table($size)

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

=head2 is_morph(0|1)

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

=head2 morpher(sub {...})

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

=head2 is_hold(0|1)

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

=head2 is_fold_over(0|1)

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

=head2 is_wrap_neg(0|1)

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

=head2 segments

returns number of segments

=head2 duration

returns total envelope duration

=head2 normalize_duration

scale segment durations, so total duration becomes 1

=head2 def

returns current envelope definition (see C<new> method)

=head2 levels(@new_levels)

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

=head2 level($pos, [$level])

get/set level at position

=head2 durs([@new_durs])

same as C<levels>, but get/sets durations for segments

=head2 dur($pos, [$level])

get/set dur at position

=head2 curves([@new_curves])

same as durs, but for curves

=head2 curve($pos, [$level])

get/set curvative at position

=head2 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])

=head1 FUNCTIONS

=over

=item 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 L<aliased>, L<as>

=back

=head1 EXAMPLES

=over

=item 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);
   
=item Interpolate

   use PDL;
   interpolate(rand(4095), sequence(4096), pdl($t)); # interpolate at random position 

=item 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;


=item 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)

=begin HTML
 
<p><img src="http://i.imgur.com/Dsqaq.png" width="478" height="263" alt="envelope example" /></p>
 
=end HTML

=back

=head1 SUPPORT

=over

=item * GitHub
 
L<http://github.com/vividsnow/Math-SegmentedEnvelope>
 
=item * Search MetaCPAN
 
L<https://metacpan.org/module/Math::SegmentedEnvelope>
 
=back
 
=head1 ACKNOWLEDGEMENTS
 
L<SuperCollider|https://supercollider.sourceforge.net>, L<Moo>
 
=head1 AUTHOR
 
Yegor Korablev <egor@cpan.org>
 
=head1 LICENSE
 
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
 
=head1 SEE ALSO
 
L<PDL>, L<Math::Fractal::Noisemaker>, L<Math::NumSeq>, L<Math::PlanePath>, L<OpenGL>, L<SDL>, L<AnyEvent>

=head1 TODO

blending,stacking,duration scaling,delaying,inverting and some common predefined envelopes
more docs,tests and examples
 
=cut