|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package Math::SegmentedEnvelope;  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # ABSTRACT: create/manage/evaluate segmented (curved) envelope  | 
| 
3
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
4472
 | 
 use Moo;  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
62404
 | 
    | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    | 
| 
4
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
6272
 | 
 use Clone 'clone';  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5668
 | 
    | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
97
 | 
    | 
| 
5
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
11
 | 
 use Carp;  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
86
 | 
    | 
| 
6
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
6
 | 
 use List::Util 'sum';  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
164
 | 
    | 
| 
7
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
7
 | 
 use constant PI => 4 * atan2(1, 1);  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
    | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
77
 | 
    | 
| 
8
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
1555
 | 
 use Exporter::Easy (OK => ['env']);  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2913
 | 
    | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
    | 
| 
9
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
 
 | 
1634
 | 
 use namespace::autoclean;  | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
36604
 | 
    | 
| 
 
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    | 
| 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has def => ( is => 'ro', default => sub {  # random by default  | 
| 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $size = int rand(5) + 3;  | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $level = shift->border_level;  | 
| 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     [   | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         [$level->[0], map(rand, (0) x $size), $level->[1]],  | 
| 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         [normalize_sum(map rand() + 0.2, (0) x ($size + 1))],  | 
| 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         [map { (rand(2) + 1) * (int(rand(2))? 1 : -1) } (0) x ($size + 1)]   | 
| 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ];  | 
| 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 });  | 
| 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has border_level => is => rw => default => sub {   # default border level for start and end  | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     [ (rand)x2 ]  | 
| 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 } => coerce => sub {  | 
| 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
      ref($_[0]) eq 'ARRAY' ? $_[0] : [($_[0])x2];  | 
| 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 };  | 
| 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has is_morph => ( is => 'rw' );  | 
| 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has morpher => (  is => 'rw', default => sub { sub { sin( $_[0] * PI / 2 ) ** 2 } } );  | 
| 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has is_hold => ( is => 'rw' );  | 
| 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has is_fold_over => ( is => 'rw' );  | 
| 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has is_wrap_neg => ( is => 'rw' );  | 
| 
30
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has _duration => ( is => 'rw' );  | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has _segments => ( is => 'rw' );  | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has _current_segment => ( is => 'rw', default => sub { 0 } );  | 
| 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has _level_diff => ( is => 'rw' );  | 
| 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has _is_neg => ( is => 'rw' );  | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has _is_asc => ( is => 'rw' );  | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has _past_segment => ( is => 'rw', default => sub { -1 } );  | 
| 
38
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has _passed_segments_duration => ( is => 'rw', default => sub { 0 } );  | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
40
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
1
  
 | 
0
 | 
 sub env { __PACKAGE__->new(@_) }  | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub BUILDARGS {  | 
| 
43
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
40438
 | 
     my ( $class, @args ) = @_;  | 
| 
44
 | 
2
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
15
 | 
     unshift @args, "def" if @args % 2 == 1;  | 
| 
45
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
54
 | 
     return { @args };  | 
| 
46
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 };  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub BUILD {  | 
| 
49
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
16
 | 
     my ($self) = @_;  | 
| 
50
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
     croak "size mismatch in envelope definition" if   | 
| 
51
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
         @{$self->def->[0]} != @{$self->def->[1]}  + 1   | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    | 
| 
52
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
             || @{$self->def->[0]} != @{$self->def->[2]} + 1  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
    | 
| 
53
 | 
2
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
3
 | 
                 || @{$self->def->[1]} != @{$self->def->[2]};  | 
| 
 
 | 
2
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
13
 | 
    | 
| 
54
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
     $self->_duration(sum(@{$self->def->[1]}));  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21
 | 
    | 
| 
55
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
     $self->_segments(scalar@{$self->def->[1]});  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
55
 | 
    | 
| 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
57
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub clean {  | 
| 
59
 | 
3
 | 
 
 | 
 
 | 
  
3
  
 | 
  
0
  
 | 
77
 | 
     my ($self) = @_;  | 
| 
60
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
     $self->_duration(sum(@{$self->def->[1]}));  | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
    | 
| 
61
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
     $self->_segments(scalar@{$self->def->[1]});  | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
10
 | 
    | 
| 
62
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
55
 | 
     $self->_current_segment(0);  | 
| 
63
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
     $self->_past_segment(-1);  | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
66
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub evaluator {  | 
| 
67
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
     my ($self) = @_;  | 
| 
68
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
 
 | 
0
 | 
     sub { $self->at(@_) };  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
69
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
70
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
71
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub at {  | 
| 
72
 | 
22
 | 
 
 | 
 
 | 
  
22
  
 | 
  
1
  
 | 
73
 | 
     my ($self, $t) = @_;  | 
| 
73
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
51
 | 
     $t = $self->wrap_pos($t);  | 
| 
74
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
78
 | 
     my ($pd,$i,$d) = (  | 
| 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $self->_passed_segments_duration,  | 
| 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $self->_current_segment  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     );  | 
| 
78
 | 
22
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
297
 | 
     while ($t < $pd && $i > 0) { $pd -= $self->def->[1]->[--$i] } # backward  | 
| 
 
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
71
 | 
    | 
| 
79
 | 
22
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
276
 | 
     $i == 0 ? $pd = 0 : $t -= $pd;  # remove duration of passed segments  | 
| 
80
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
68
 | 
     while ($i < $self->_segments) { # forward - determine segment and cache it for next time  | 
| 
81
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
146
 | 
         $d = $self->def->[1]->[$i]; # set current segment duration + error  | 
| 
82
 | 
41
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
146
 | 
         if ($t > $d && $i != $self->_segments - 1) { # t passed this segment, so remove this segment duration  | 
| 
83
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
             $t -= $d; $pd += $d; $i++; next;  | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
    | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
    | 
| 
 
 | 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
87
 | 
    | 
| 
84
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  # $t is in current segment  | 
| 
85
 | 
22
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
52
 | 
             $t = $d if $t > $d;  | 
| 
86
 | 
22
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
85
 | 
             $i = $self->update_current_segment($i) unless $i == $self->_past_segment; last;  | 
| 
 
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
37
 | 
    | 
| 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
88
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
89
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # print "r:$i\tt:$t\td:$d\tp:$pd";  | 
| 
90
 | 
22
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
71
 | 
     $self->_passed_segments_duration($pd) if $pd != $self->_passed_segments_duration;  | 
| 
91
 | 
22
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
53
 | 
     $self->_current_segment($i) if $i != $self->_current_segment;  | 
| 
92
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     abs( # result value  | 
| 
93
 | 
22
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
186
 | 
         $self->wrap_value(abs(( $self->_is_neg ? $d - $t : $t ) / $d))  | 
| 
94
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         ** abs($self->def->[2]->[$i])  | 
| 
95
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         * $self->_is_asc  | 
| 
96
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         + $self->_is_neg  | 
| 
97
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ) * $self->_level_diff + $self->def->[0]->[$i];  | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     #print "\t$t\n"; $t;  | 
| 
99
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
101
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub wrap_value {  | 
| 
102
 | 
22
 | 
 
 | 
 
 | 
  
22
  
 | 
  
0
  
 | 
36
 | 
     my ($self) = @_;  | 
| 
103
 | 
22
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
3281
 | 
     $self->is_morph ? $self->morpher->($_[1]) : $_[1]; # value smooth or whatever      | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub wrap_pos {  | 
| 
107
 | 
22
 | 
 
 | 
 
 | 
  
22
  
 | 
  
0
  
 | 
71
 | 
     my ($self,$t) = @_;  | 
| 
108
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
45
 | 
     my $total = $self->_duration;  | 
| 
109
 | 
22
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
82
 | 
     if ($self->is_hold) {  | 
| 
110
 | 
10
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
44
 | 
         $t > 0 ? ( $t > $total ? $total : $t ) : 0  | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
112
 | 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
         my $at = abs($t);  | 
| 
113
 | 
12
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
29
 | 
         if ($at > $total) {  | 
| 
114
 | 
4
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
43
 | 
             if  ($self->is_fold_over && int($at/$total) % 2 == ( $t < 0 && $self->is_wrap_neg ? 0 : 1 )) { #fold  | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
115
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
                 ( 1 - ( ($at / $total) - int($at / $total) ) ) * $total;  | 
| 
116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } else { # wrap  | 
| 
117
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
                 ( ($at / $total) - int($at / $total) ) * $total;  | 
| 
118
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
119
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
         } else { $at }  | 
| 
120
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     };  | 
| 
121
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
122
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
123
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub update_current_segment {  | 
| 
124
 | 
12
 | 
 
 | 
 
 | 
  
12
  
 | 
  
0
  
 | 
241
 | 
     my ($self, $i) = @_;  | 
| 
125
 | 
12
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
47
 | 
     $i = $self->_current_segment(defined($i) ? $i : ());  | 
| 
126
 | 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
45
 | 
     $self->_level_diff($self->level($i+1) - $self->level($i));  | 
| 
127
 | 
12
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
46
 | 
     $self->_is_neg($self->curve($i) < 0 ? 1 : 0);  | 
| 
128
 | 
12
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
72
 | 
     $self->_is_asc($self->_level_diff < 0 || $self->_is_neg ? -1 : 1);  | 
| 
129
 | 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
30
 | 
     $self->_past_segment($i);  | 
| 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
131
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
132
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub level {  | 
| 
133
 | 
27
 | 
 
 | 
 
 | 
  
27
  
 | 
  
1
  
 | 
121
 | 
     my $self = shift;  | 
| 
134
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
61
 | 
     my $r = $self->def_part_value(0, @_);  | 
| 
135
 | 
27
 | 
  
 50
  
 | 
  
100
  
 | 
 
 | 
 
 | 
84
 | 
     $self->update_current_segment if @_ > 1 && abs($self->_current_segment - ($_[0] >= 0 ? $_[0] : $self->_segments + $_[0])) <= 1;  | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
136
 | 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
72
 | 
     $r;  | 
| 
137
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
138
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
139
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub levels {   | 
| 
140
 | 
6
 | 
 
 | 
 
 | 
  
6
  
 | 
  
1
  
 | 
10
 | 
     my $self = shift;  | 
| 
141
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
     my @r = $self->def_part(0, @_);  | 
| 
142
 | 
6
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
22
 | 
     $self->update_current_segment if @_ > 0;  | 
| 
143
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
41
 | 
     @r;  | 
| 
144
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
145
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
146
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub dur {  | 
| 
147
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
  
1
  
 | 
2
 | 
     my $self = shift;  | 
| 
148
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
     my $r = $self->def_part_value(1, @_);  | 
| 
149
 | 
1
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
6
 | 
     $self->clean if @_ > 1;  | 
| 
150
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
     $r;  | 
| 
151
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
152
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
153
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub durs {  | 
| 
154
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
  
1
  
 | 
13
 | 
     my $self = shift;  | 
| 
155
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
     my @r = $self->def_part(1, @_);  | 
| 
156
 | 
8
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
3220
 | 
     $self->clean if @_ > 1;  | 
| 
157
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
64
 | 
     @r;      | 
| 
158
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
160
 | 
23
 | 
 
 | 
 
 | 
  
23
  
 | 
  
1
  
 | 
264
 | 
 sub duration { shift->_duration }  | 
| 
161
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
  
1
  
 | 
32
 | 
 sub segments { shift->_segments }  | 
| 
162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
163
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub curve {  | 
| 
164
 | 
13
 | 
 
 | 
 
 | 
  
13
  
 | 
  
1
  
 | 
94
 | 
     my $self = shift;  | 
| 
165
 | 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
40
 | 
     my $r = $self->def_part_value(2, @_);  | 
| 
166
 | 
13
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
62
 | 
     $self->update_current_segment if @_ > 1 && $self->_current_segment == $_[0];  | 
| 
167
 | 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
268
 | 
     $r;  | 
| 
168
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
169
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
170
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub curves {   | 
| 
171
 | 
6
 | 
 
 | 
 
 | 
  
6
  
 | 
  
1
  
 | 
82
 | 
     my $self = shift;  | 
| 
172
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
     my @r = $self->def_part(2, @_);  | 
| 
173
 | 
6
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
21
 | 
     $self->update_current_segment if @_ > 0;  | 
| 
174
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
98
 | 
     @r;  | 
| 
175
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
176
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
177
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub def_part {  | 
| 
178
 | 
20
 | 
 
 | 
 
 | 
  
20
  
 | 
  
0
  
 | 
39
 | 
     my ($self, $p, @values) = @_;  | 
| 
179
 | 
20
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
51
 | 
     (@values == @{$self->def->[$p]} ? $self->def->[$p] = [@values] : carp "size mismatch against initial definition") if @values;  | 
| 
 
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
33
 | 
    | 
| 
180
 | 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
78
 | 
     @{$self->def->[$p]};  | 
| 
 
 | 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
121
 | 
    | 
| 
181
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
183
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub def_part_value {  | 
| 
184
 | 
41
 | 
 
 | 
 
 | 
  
41
  
 | 
  
0
  
 | 
386
 | 
     my ($self, $p, $at, $value) = @_;  | 
| 
185
 | 
41
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
273
 | 
     croak "no such index '$at' in definition part '$p'" if !defined($at) || !exists($self->def->[$p]->[$at]);  | 
| 
186
 | 
41
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
156
 | 
     $self->def->[$p]->[$at] = $value if $value;  | 
| 
187
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
110
 | 
     $self->def->[$p]->[$at];  | 
| 
188
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
189
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
190
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub static { # make immutable evaluator from current params  | 
| 
191
 | 
4
 | 
 
 | 
 
 | 
  
4
  
 | 
  
1
  
 | 
79
 | 
     my ($self) = @_;  | 
| 
192
 | 
4
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
22
 | 
     my ($lev, $dur, $cur, $is_smooth, $is_hold, $is_fold_over, $is_wrap_neg, $total) = (  | 
| 
193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         [$self->levels], [$self->durs], [$self->curves], $self->is_morph && clone($self->morpher),  | 
| 
194
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $self->is_hold, $self->is_fold_over, $self->is_wrap_neg, $self->duration  | 
| 
195
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     );  | 
| 
196
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
     my ($i, $pd, $cs, $level_diff, $is_asc, $is_neg, $d) = (0, 0, -1); # segment index and its data  | 
| 
197
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $segment_data = sub {   | 
| 
198
 | 
10
 | 
 
 | 
 
 | 
  
10
  
 | 
 
 | 
31
 | 
         $level_diff = $lev->[$i+1] - $lev->[$i];  | 
| 
199
 | 
10
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
31
 | 
         $is_neg = $cur->[$i] < 0 ? 1 : 0;  | 
| 
200
 | 
10
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
40
 | 
         $is_asc = $level_diff < 0 || $is_neg ? -1 : 1;  | 
| 
201
 | 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
         $cs = $i;  | 
| 
202
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35
 | 
     };  | 
| 
203
 | 
4
 | 
  
 50
  
 | 
 
 | 
  
13
  
 | 
 
 | 
27
 | 
     my $wrap_value = $is_smooth ? ( ref($is_smooth) eq 'CODE' ? $is_smooth : sub { sin( PI / 2 * $_[0] ) } ) : sub { $_[0] }; # value smooth or whatever  | 
| 
 
 | 
0
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
111
 | 
    | 
| 
204
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $wrap_pos = $is_hold ? sub {  | 
| 
205
 | 
10
 | 
  
100
  
 | 
 
 | 
  
10
  
 | 
 
 | 
43
 | 
         $_[0] > 0 ? ( $_[0] > $total ? $total : $_[0] ) : 0;  | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
206
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } : sub {  | 
| 
207
 | 
1028
 | 
 
 | 
 
 | 
  
1028
  
 | 
 
 | 
1673
 | 
         my $t = abs($_[0]);  | 
| 
208
 | 
1028
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
2096
 | 
         if ($t > $total) { #fold  | 
| 
209
 | 
1
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
19
 | 
             if ($is_fold_over && int($t/$total) % 2 == ($_[0] < 0 && $is_wrap_neg ? 0 : 1)) {  | 
| 
 
 | 
 
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
210
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
                 (1 - (($t / $total) - int( $t / $total ))) * $total;  | 
| 
211
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } else { # wrap  | 
| 
212
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
                 (($t / $total) - int( $t / $total )) * $total;  | 
| 
213
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
214
 | 
1027
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2200
 | 
         } else { $t }  | 
| 
215
 | 
4
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
249
 | 
     };  | 
| 
216
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
     my $last_segment = @$dur - 1;  | 
| 
217
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     sub {  | 
| 
218
 | 
1038
 | 
 
 | 
 
 | 
  
1038
  
 | 
 
 | 
2320
 | 
         my $t = $wrap_pos->($_[0]);  | 
| 
219
 | 
1038
 | 
 
 | 
  
 66
  
 | 
 
 | 
 
 | 
6110
 | 
         while ($t < $pd && $i > 0) { $pd -= $dur->[--$i] } # backward  | 
| 
 
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
33
 | 
    | 
| 
220
 | 
1038
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
2496
 | 
         $i == 0 ? $pd = 0 : $t -= $pd;  # remove duration of passed segments  | 
| 
221
 | 
1038
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2771
 | 
         while ($i <= $last_segment) { # forward - determine segment and cache it for next tiem  | 
| 
222
 | 
1059
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1619
 | 
             $d = $dur->[$i]; # set current segment duration  | 
| 
223
 | 
1059
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
3657
 | 
             if ($t > $d && $i != $last_segment) { # t passed this segment, so remove this segment duration  | 
| 
224
 | 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
25
 | 
                 $t -= $d; $pd += $d; $i++; next;  | 
| 
 
 | 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
28
 | 
    | 
| 
 
 | 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
23
 | 
    | 
| 
 
 | 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
44
 | 
    | 
| 
225
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } else {  # $t is in current segment  | 
| 
226
 | 
1038
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
7594
 | 
                 $t = $d if $t > $d;  | 
| 
227
 | 
1038
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
2166
 | 
                 $segment_data->() unless $i == $cs; last;  | 
| 
 
 | 
1038
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1623
 | 
    | 
| 
228
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
229
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
230
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         #print "r:$i\tt:$t\td:$d\tp:$pd";  | 
| 
231
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         abs( # result value  | 
| 
232
 | 
1038
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
2666
 | 
             $wrap_value->(( $is_neg ? ($d - $t) : $t ) / $d)  | 
| 
233
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             ** abs($cur->[$i])  | 
| 
234
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             * $is_asc  | 
| 
235
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             + $is_neg  | 
| 
236
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         ) * $level_diff + $lev->[$i];  | 
| 
237
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         #print "\t$t\n"; $t;  | 
| 
238
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
239
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
655
 | 
 }  | 
| 
240
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
241
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub table { # create lookup table of specified size, loops and range  | 
| 
242
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
  
1
  
 | 
7
 | 
     my ($self, $size, $loop, $from, $to) = @_;  | 
| 
243
 | 
1
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
4
 | 
     $size ||= 1024;  | 
| 
244
 | 
1
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
7
 | 
     $loop ||= 1;  | 
| 
245
 | 
1
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
5
 | 
     $from ||= 0;  | 
| 
246
 | 
1
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
6
 | 
     $to   ||= $self->duration;  | 
| 
247
 | 
1
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
4
 | 
     croak "table size should be >= 1" if $size <= 0;  | 
| 
248
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
79
 | 
     my $s = $self->static;  | 
| 
249
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
     my $range = $to - $from;  | 
| 
250
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
     my $lp = $loop / $size;  | 
| 
251
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
     my $p;  | 
| 
252
 | 
1024
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1507
 | 
     map {   | 
| 
253
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
75
 | 
         $p = $_ * $lp;  | 
| 
254
 | 
1024
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2619
 | 
         $s->($from + $range * ($p - int $p));  | 
| 
255
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } 0..$size-1;  | 
| 
256
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
257
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
258
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub normalize_duration {  | 
| 
259
 | 
1
 | 
 
 | 
 
 | 
  
1
  
 | 
  
1
  
 | 
8
 | 
     my ($self) = @_;  | 
| 
260
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
     $self->durs(normalize_sum($self->durs));  | 
| 
261
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
     $self;  | 
| 
262
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
263
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
264
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub normalize_sum {  | 
| 
265
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
0
  
 | 
92
 | 
     my $s = sum@_;  | 
| 
266
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
     map $_/$s, @_;  | 
| 
267
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
268
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
269
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # TODO utility methods  | 
| 
270
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
 
 | 
 sub stack {} # concat?  | 
| 
271
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
 
 | 
 sub blend {}  | 
| 
272
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
 
 | 
 sub delay {}  | 
| 
273
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # TODO some usual envelopes  | 
| 
274
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
 
 | 
 sub adsr {}  | 
| 
275
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
 
 | 
 sub asr {}  | 
| 
276
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
 
 | 
 sub cutoff {}  | 
| 
277
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
 
 | 
 sub perc {}  | 
| 
278
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
279
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  |