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