line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
{ |
2
|
|
|
|
|
|
|
package Audio::Nama::Effect; |
3
|
1
|
|
|
1
|
|
4
|
use Modern::Perl; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
6
|
|
4
|
1
|
|
|
1
|
|
973
|
use List::MoreUtils qw(first_index insert_after_string); |
|
1
|
|
|
|
|
11961
|
|
|
1
|
|
|
|
|
7
|
|
5
|
1
|
|
|
1
|
|
653
|
use Carp qw(carp cluck croak confess); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
62
|
|
6
|
1
|
|
|
1
|
|
5
|
use Data::Dumper::Concise; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
55
|
|
7
|
1
|
|
|
1
|
|
5
|
use Audio::Nama::Assign qw(json_out); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
47
|
|
8
|
1
|
|
|
1
|
|
5
|
use Audio::Nama::Log qw(logsub logpkg); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
54
|
|
9
|
1
|
|
|
|
|
169
|
use Audio::Nama::Globals qw( |
10
|
|
|
|
|
|
|
$fx |
11
|
|
|
|
|
|
|
$fx_cache |
12
|
|
|
|
|
|
|
$ui |
13
|
|
|
|
|
|
|
%ti |
14
|
|
|
|
|
|
|
%tn |
15
|
|
|
|
|
|
|
%bn |
16
|
|
|
|
|
|
|
%en |
17
|
|
|
|
|
|
|
$config |
18
|
|
|
|
|
|
|
$setup |
19
|
|
|
|
|
|
|
$project |
20
|
|
|
|
|
|
|
$this_engine |
21
|
1
|
|
|
1
|
|
5
|
$this_track); |
|
1
|
|
|
|
|
2
|
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
|
24
|
1
|
|
|
|
|
8
|
use Audio::Nama::Object qw( |
25
|
|
|
|
|
|
|
id |
26
|
|
|
|
|
|
|
type |
27
|
|
|
|
|
|
|
chain |
28
|
|
|
|
|
|
|
class |
29
|
|
|
|
|
|
|
params |
30
|
|
|
|
|
|
|
params_log |
31
|
|
|
|
|
|
|
display |
32
|
|
|
|
|
|
|
parent |
33
|
|
|
|
|
|
|
owns |
34
|
|
|
|
|
|
|
bypassed |
35
|
|
|
|
|
|
|
name |
36
|
|
|
|
|
|
|
surname |
37
|
|
|
|
|
|
|
|
38
|
1
|
|
|
1
|
|
6
|
); |
|
1
|
|
|
|
|
2
|
|
39
|
|
|
|
|
|
|
*this_op = \&Audio::Nama::this_op; |
40
|
|
|
|
|
|
|
*this_param = \&Audio::Nama::this_param; |
41
|
|
|
|
|
|
|
*this_stepsize = \&Audio::Nama::this_stepsize; |
42
|
|
|
|
|
|
|
our %by_id; |
43
|
|
|
|
|
|
|
our $AUTOLOAD; |
44
|
|
|
|
|
|
|
import_engine_subs(); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub initialize { |
47
|
|
|
|
|
|
|
|
48
|
0
|
|
|
0
|
0
|
0
|
%by_id = () ; |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
# effect variables - no object code (yet) |
51
|
0
|
|
|
|
|
0
|
$fx->{id_counter} = "A"; # autoincrement counter |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# volume settings |
54
|
0
|
|
|
|
|
0
|
$fx->{muted} = []; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
sub AUTOLOAD { |
57
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
58
|
|
|
|
|
|
|
#say "got self: $self", Audio::Nama::Dumper $self; |
59
|
0
|
0
|
|
|
|
0
|
die 'not object' unless ref $self; |
60
|
|
|
|
|
|
|
# get tail of method call |
61
|
0
|
|
|
|
|
0
|
my ($call) = $AUTOLOAD =~ /([^:]+)$/; |
62
|
|
|
|
|
|
|
# see if this can be satisfied by a field from |
63
|
|
|
|
|
|
|
# the corresponding effects registry entry |
64
|
0
|
0
|
|
|
|
0
|
$call = 'name' if $call eq 'fxname'; |
65
|
0
|
|
|
|
|
0
|
$self->about->{$call} |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
0
|
|
|
sub DESTROY {} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub new { |
70
|
0
|
|
|
0
|
0
|
0
|
my ($class, %args) = @_; |
71
|
|
|
|
|
|
|
|
72
|
0
|
|
|
|
|
0
|
my $is_restore = $args{restore}; |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
# remove arguments that won't be part of object |
75
|
0
|
|
|
|
|
0
|
delete $args{$_} for qw(restore before); |
76
|
|
|
|
|
|
|
|
77
|
0
|
|
|
|
|
0
|
my $self; |
78
|
|
|
|
|
|
|
|
79
|
0
|
|
|
|
|
0
|
my $id = $args{id}; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
# return an existing object that has this ID |
82
|
|
|
|
|
|
|
# Why do this instead of throw an exception? |
83
|
0
|
0
|
0
|
|
|
0
|
if ($id and $self = fxn($id)){ |
84
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug',"$id: returning existing object, constructor arguments ignored"); |
85
|
0
|
|
|
|
|
0
|
return $self |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
0
|
|
|
|
|
0
|
my $i = effect_index($args{type}); |
89
|
0
|
0
|
|
|
|
0
|
defined $i or confess "$args{type}: plugin not found."; |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# allocate effect ID if we don't have one yet |
92
|
|
|
|
|
|
|
# |
93
|
0
|
0
|
|
|
|
0
|
my $how_allocated = $id ? 'recycled' : 'issued'; |
94
|
0
|
|
0
|
|
|
0
|
$id //= new_effect_id(); |
95
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug',"$id: effect ID $how_allocated for track $args{chain}"); |
96
|
|
|
|
|
|
|
|
97
|
0
|
|
|
|
|
0
|
$args{id} = $id; |
98
|
0
|
|
0
|
|
|
0
|
$args{display} //= $fx_cache->{registry}->[$i]->{display}; |
99
|
0
|
|
0
|
|
|
0
|
$args{owns} //= []; |
100
|
|
|
|
|
|
|
|
101
|
0
|
|
|
|
|
0
|
my $track = $ti{$args{chain}}; |
102
|
|
|
|
|
|
|
|
103
|
0
|
|
|
|
|
0
|
my $parent_id = $args{parent}; |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# set defaults for effects without values provided |
106
|
|
|
|
|
|
|
# but skip controllers |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# append_effect() also assigns defaults, so why not |
109
|
|
|
|
|
|
|
# do all the assigning here? |
110
|
|
|
|
|
|
|
|
111
|
0
|
0
|
0
|
|
|
0
|
if (! $parent_id and ! $args{params}){ |
112
|
0
|
|
|
|
|
0
|
my @vals; |
113
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug', "no settings found, loading defaults if present"); |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
# if the effect is a controller (has a parent), we don't |
116
|
|
|
|
|
|
|
# initialize the first parameter (the control target) |
117
|
|
|
|
|
|
|
|
118
|
0
|
|
|
|
|
0
|
for my $j (0..$fx_cache->{registry}->[$i]->{count} - 1) { |
119
|
|
|
|
|
|
|
|
120
|
0
|
|
|
|
|
0
|
push @vals, $fx_cache->{registry}->[$i]->{params}->[$j]->{default}; |
121
|
|
|
|
|
|
|
} |
122
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug', "copid: $id defaults: @vals"); |
123
|
0
|
|
|
|
|
0
|
$args{params} = \@vals; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug', "effect args: ",Dumper \%args); |
127
|
|
|
|
|
|
|
|
128
|
0
|
|
|
|
|
0
|
$self = bless \%args, $class; |
129
|
0
|
|
|
|
|
0
|
$by_id{$self->id} = $self; |
130
|
|
|
|
|
|
|
|
131
|
0
|
0
|
|
|
|
0
|
return $self if $is_restore; |
132
|
|
|
|
|
|
|
|
133
|
0
|
0
|
|
|
|
0
|
if ($parent_id) { |
134
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug', "parent found: $parent_id"); |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
# store relationship |
137
|
|
|
|
|
|
|
|
138
|
0
|
|
|
|
|
0
|
my $parent = fxn($parent_id); |
139
|
0
|
|
|
|
|
0
|
my $owns = $parent->owns; |
140
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug',"parent owns @$owns"); |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
# register effect_id with parent unless it is already there |
143
|
0
|
0
|
|
|
|
0
|
push @$owns, $id unless grep { $id eq $_ } @$owns; |
|
0
|
|
|
|
|
0
|
|
144
|
|
|
|
|
|
|
|
145
|
0
|
|
|
0
|
|
0
|
logpkg(__FILE__,__LINE__,'debug',sub{join " ", "my attributes:", json_out($self->as_hash)}); |
|
0
|
|
|
|
|
0
|
|
146
|
|
|
|
|
|
|
# find position of parent id in the track ops array |
147
|
|
|
|
|
|
|
# and insert child id immediately afterwards |
148
|
|
|
|
|
|
|
# unless already present |
149
|
|
|
|
|
|
|
|
150
|
0
|
|
|
|
|
0
|
insert_after_string($parent_id, $id, @{$track->ops}) |
151
|
0
|
0
|
|
|
|
0
|
unless grep {$id eq $_} @{$track->ops} |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
else { |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
# append effect_id to track list unless already present |
156
|
0
|
0
|
|
|
|
0
|
push @{$track->ops}, $id unless grep {$id eq $_} @{$track->ops} |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
157
|
|
|
|
|
|
|
} |
158
|
0
|
|
|
|
|
0
|
$self |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# fx method delivers hash, previously via $fx->{ applied}->{$id} |
162
|
|
|
|
|
|
|
# TODO: get rid of this entirely |
163
|
0
|
|
|
0
|
0
|
0
|
sub fx { my $self = shift; $self } |
|
0
|
|
|
|
|
0
|
|
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# provide object |
166
|
|
|
|
|
|
|
{ |
167
|
1
|
|
|
1
|
|
6
|
no warnings 'redefine'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
105
|
|
168
|
|
|
|
|
|
|
sub parent { |
169
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
170
|
0
|
|
|
|
|
0
|
fxn($self->{parent}); |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
sub is_read_only { |
175
|
0
|
|
|
0
|
0
|
0
|
my ($self, $param) = @_; |
176
|
1
|
|
|
1
|
|
5
|
no warnings 'uninitialized'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
203
|
|
177
|
0
|
|
|
|
|
0
|
$self->about->{params}->[$param]->{dir} eq 'output' |
178
|
|
|
|
|
|
|
} |
179
|
0
|
|
|
0
|
0
|
0
|
sub remove_name { my $self = shift; delete $self->{name} } |
|
0
|
|
|
|
|
0
|
|
180
|
0
|
|
|
0
|
0
|
0
|
sub set_name { my $self = shift; $self->{name} = shift } |
|
0
|
|
|
|
|
0
|
|
181
|
0
|
|
|
0
|
0
|
0
|
sub set_surname { my $self = shift; $self->{surname} = shift} |
|
0
|
|
|
|
|
0
|
|
182
|
0
|
|
|
0
|
0
|
0
|
sub is_controller { my $self = shift; $self->parent } |
|
0
|
|
|
|
|
0
|
|
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
sub has_read_only_param { |
185
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
186
|
1
|
|
|
1
|
|
5
|
no warnings 'uninitialized'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
671
|
|
187
|
0
|
|
|
|
|
0
|
my $entry = $self->about; |
188
|
0
|
|
|
|
|
0
|
for(0..scalar @{$entry->{params}} - 1) |
|
0
|
|
|
|
|
0
|
|
189
|
|
|
|
|
|
|
{ |
190
|
0
|
0
|
|
|
|
0
|
return 1 if $entry->{params}->[$_]->{dir} eq 'output' |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
sub registry_index { |
195
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
196
|
0
|
|
|
|
|
0
|
$fx_cache->{full_label_to_index}->{ $self->type }; |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
sub ecasound_controller_index { |
199
|
0
|
|
|
0
|
0
|
0
|
logsub("&ecasound_controller_index"); |
200
|
0
|
|
|
|
|
0
|
my $self = shift; |
201
|
0
|
|
|
|
|
0
|
my $id = $self->id; |
202
|
0
|
|
|
|
|
0
|
my $chain = $self->chain; |
203
|
0
|
|
|
|
|
0
|
my $track = $ti{$chain}; |
204
|
0
|
|
|
|
|
0
|
my @ops = @{$track->ops}; |
|
0
|
|
|
|
|
0
|
|
205
|
0
|
|
|
|
|
0
|
my $operator_count = 0; |
206
|
0
|
|
|
|
|
0
|
my $position; |
207
|
0
|
|
|
|
|
0
|
for my $i (0..scalar @ops - 1) { |
208
|
0
|
0
|
|
|
|
0
|
$position = $i, last if $ops[$i] eq $id; |
209
|
0
|
0
|
|
|
|
0
|
$operator_count++ if ! Audio::Nama::fxn($ops[$i])->is_controller; |
210
|
|
|
|
|
|
|
} |
211
|
0
|
|
|
|
|
0
|
$position -= $operator_count; # skip operators |
212
|
0
|
|
|
|
|
0
|
++$position; # translates 0th to chain-position 1 |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
sub ecasound_operator_index { # does not include offset |
215
|
0
|
|
|
0
|
0
|
0
|
logsub("&ecasound_operator_index"); |
216
|
0
|
|
|
|
|
0
|
my $self = shift; |
217
|
0
|
|
|
|
|
0
|
my $id = $self->id; |
218
|
0
|
|
|
|
|
0
|
my $chain = $self->chain; |
219
|
0
|
|
|
|
|
0
|
my $track = $ti{$chain}; |
220
|
0
|
|
|
|
|
0
|
my @ops = @{$track->ops}; |
|
0
|
|
|
|
|
0
|
|
221
|
0
|
|
|
|
|
0
|
my $controller_count = 0; |
222
|
0
|
|
|
|
|
0
|
my $position; |
223
|
0
|
|
|
|
|
0
|
for my $i (0..scalar @ops - 1) { |
224
|
0
|
0
|
|
|
|
0
|
$position = $i, last if $ops[$i] eq $id; |
225
|
0
|
0
|
|
|
|
0
|
$controller_count++ if Audio::Nama::fxn($ops[$i])->is_controller; |
226
|
|
|
|
|
|
|
} |
227
|
0
|
|
|
|
|
0
|
$position -= $controller_count; # skip controllers |
228
|
0
|
|
|
|
|
0
|
++$position; # translates 0th to chain-position 1 |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
sub ecasound_effect_index { |
231
|
0
|
|
|
0
|
0
|
0
|
logsub("&ecasound_effect_index"); |
232
|
0
|
|
|
|
|
0
|
my $self = shift; |
233
|
0
|
|
|
|
|
0
|
my $n = $self->chain; |
234
|
0
|
|
|
|
|
0
|
my $id = $self->id; |
235
|
0
|
|
|
|
|
0
|
my $opcount = 0; |
236
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug', "id: $id, n: $n, ops: @{ $ti{$n}->ops }" ); |
|
0
|
|
|
|
|
0
|
|
237
|
0
|
|
|
|
|
0
|
for my $op (@{ $ti{$n}->ops }) { |
|
0
|
|
|
|
|
0
|
|
238
|
|
|
|
|
|
|
# increment only for ops, not controllers |
239
|
0
|
0
|
|
|
|
0
|
next if $self->is_controller; |
240
|
0
|
|
|
|
|
0
|
++$opcount; # first index is 1 |
241
|
0
|
0
|
|
|
|
0
|
last if $op eq $id |
242
|
|
|
|
|
|
|
} |
243
|
1
|
|
|
1
|
|
5
|
no warnings 'uninitialized'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
636
|
|
244
|
0
|
|
|
|
|
0
|
$self->offset + $opcount; |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
sub track_effect_index { # the position of the ID in the track's op array |
247
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
248
|
0
|
|
|
|
|
0
|
my $id = $self->id; |
249
|
0
|
|
|
0
|
|
0
|
my $pos = first_index {$id eq $_} @{$self->track->ops} ; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
250
|
0
|
|
|
|
|
0
|
$pos |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
sub sync_one_effect { |
253
|
0
|
|
|
0
|
0
|
0
|
my $self= shift; |
254
|
0
|
|
|
|
|
0
|
my $chain = $self->chain; |
255
|
0
|
|
|
|
|
0
|
Audio::Nama::eval_iam("c-select $chain"); |
256
|
0
|
|
|
|
|
0
|
Audio::Nama::eval_iam("cop-select " .( $self->offset + $self->ecasound_operator_index ) ); |
257
|
0
|
|
|
|
|
0
|
$self->set(params => get_ecasound_cop_params( scalar @{$self->params} )); |
|
0
|
|
|
|
|
0
|
|
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
sub offset { |
260
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
261
|
0
|
|
|
|
|
0
|
$fx->{offset}->{$self->chain} |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
sub root_parent { |
264
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
265
|
0
|
|
|
|
|
0
|
my $parent = $self->parent; |
266
|
0
|
0
|
0
|
|
|
0
|
$parent and $parent->parent or $parent or $self |
|
|
|
0
|
|
|
|
|
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
sub about { |
269
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
270
|
0
|
|
|
|
|
0
|
$fx_cache->{registry}->[$self->registry_index] |
271
|
|
|
|
|
|
|
} |
272
|
0
|
|
|
0
|
0
|
0
|
sub track { $ti{$_[0]->chain} } |
273
|
0
|
|
|
0
|
0
|
0
|
sub trackname { $_[0]->track->name } |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
sub ladspa_id { |
276
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
277
|
0
|
|
|
|
|
0
|
$Audio::Nama::fx_cache->{ladspa_label_to_unique_id}->{$self->type} |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
sub nameline { |
280
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
281
|
0
|
|
|
|
|
0
|
my @attr_keys = qw( name surname fxname type ladspa_id bypassed trackname); |
282
|
0
|
|
|
|
|
0
|
my $nameline = $self->id. ": ". join q(, ), grep{$_} map{$self->$_} @attr_keys; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
283
|
0
|
|
|
|
|
0
|
$nameline .= "\n"; |
284
|
0
|
|
|
|
|
0
|
$nameline |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
sub _effect_index { |
287
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
288
|
0
|
|
|
|
|
0
|
effect_index($self->type) |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
sub _modify_effect { |
291
|
0
|
|
|
0
|
|
0
|
my ($self, $parameter, $value, $sign) = @_; |
292
|
1
|
|
|
1
|
|
12
|
no warnings 'uninitialized'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1624
|
|
293
|
0
|
|
|
|
|
0
|
my $op_id = $self->id; |
294
|
|
|
|
|
|
|
|
295
|
0
|
|
|
|
|
0
|
$parameter--; # convert to zero-based |
296
|
0
|
|
|
|
|
0
|
my $code = $self->type; |
297
|
0
|
|
|
|
|
0
|
my $i = $self->_effect_index; |
298
|
0
|
0
|
|
|
|
0
|
defined $i or confess "undefined effect code for $op_id: ",Audio::Nama::Dumper $self; |
299
|
0
|
|
|
|
|
0
|
my $parameter_count = scalar @{ $self->about->{params} }; |
|
0
|
|
|
|
|
0
|
|
300
|
0
|
0
|
0
|
|
|
0
|
Audio::Nama::pager("$op_id: parameter (", $parameter + 1, ") out of range, skipping.\n"), return |
301
|
|
|
|
|
|
|
unless ($parameter >= 0 and $parameter < $parameter_count); |
302
|
0
|
0
|
|
|
|
0
|
Audio::Nama::pager("$op_id: parameter $parameter is read-only, skipping\n"), return |
303
|
|
|
|
|
|
|
if $self->is_read_only($parameter); |
304
|
0
|
|
|
|
|
0
|
my $new_value; |
305
|
0
|
0
|
|
|
|
0
|
if ($sign) { |
306
|
0
|
|
|
|
|
0
|
$new_value = eval |
307
|
|
|
|
|
|
|
( join " ", |
308
|
|
|
|
|
|
|
$self->params->[$parameter], |
309
|
|
|
|
|
|
|
$sign, |
310
|
|
|
|
|
|
|
$value |
311
|
|
|
|
|
|
|
); |
312
|
|
|
|
|
|
|
} |
313
|
0
|
|
|
|
|
0
|
else { $new_value = $value } |
314
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug', "id $op_id p: $parameter, sign: $sign value: $value"); |
315
|
0
|
|
|
|
|
0
|
update_effect( |
316
|
|
|
|
|
|
|
$op_id, |
317
|
|
|
|
|
|
|
$parameter, |
318
|
|
|
|
|
|
|
$new_value); |
319
|
0
|
|
|
|
|
0
|
1 |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
sub _remove_effect { |
322
|
0
|
|
|
0
|
|
0
|
logsub("&_remove_effect"); |
323
|
0
|
|
|
|
|
0
|
my $self = shift; |
324
|
0
|
|
|
|
|
0
|
my $id = $self->id; |
325
|
0
|
|
|
|
|
0
|
my $n = $self->chain; |
326
|
0
|
|
|
|
|
0
|
my $parent = $self->parent; |
327
|
0
|
|
|
|
|
0
|
my $owns = $self->owns; |
328
|
0
|
0
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug', "id: $id", ($parent ? ". parent: ".$parent->id : '' )); |
329
|
|
|
|
|
|
|
|
330
|
0
|
0
|
|
|
|
0
|
my $object = $parent ? q(controller) : q(chain operator); |
331
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug', qq(ready to remove $object "$id" from track "$n")); |
332
|
|
|
|
|
|
|
|
333
|
0
|
|
|
|
|
0
|
$ui->remove_effect_gui($id); |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# recursively remove children |
336
|
|
|
|
|
|
|
|
337
|
0
|
0
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug',"children found: ". join ",",@$owns) if defined $owns; |
338
|
0
|
0
|
|
|
|
0
|
map{ remove_effect($_) } @$owns if defined $owns; |
|
0
|
|
|
|
|
0
|
|
339
|
|
|
|
|
|
|
; |
340
|
|
|
|
|
|
|
# remove chain operator |
341
|
|
|
|
|
|
|
|
342
|
0
|
0
|
|
|
|
0
|
if ( ! $parent ) { remove_op($id) } |
|
0
|
|
|
|
|
0
|
|
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
# remove controller |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
else { |
347
|
|
|
|
|
|
|
|
348
|
0
|
|
|
|
|
0
|
remove_op($id); |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
# remove parent ownership of deleted controller |
351
|
|
|
|
|
|
|
|
352
|
0
|
|
|
|
|
0
|
my $parent_owns = $parent->owns; |
353
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug',"parent $parent owns: ". join ",", @$parent_owns); |
354
|
|
|
|
|
|
|
|
355
|
0
|
|
|
|
|
0
|
@$parent_owns = (grep {$_ ne $id} @$parent_owns); |
|
0
|
|
|
|
|
0
|
|
356
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug',"parent $parent new owns list: ". join ",", @$parent_owns); |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
# remove effect ID from track |
360
|
|
|
|
|
|
|
|
361
|
0
|
0
|
|
|
|
0
|
if( my $track = $ti{$n} ){ |
362
|
0
|
|
|
|
|
0
|
my @ops_list = @{$track->ops}; |
|
0
|
|
|
|
|
0
|
|
363
|
|
|
|
|
|
|
#say "ops_list: @ops_list"; |
364
|
0
|
|
|
|
|
0
|
my $perl_version = $^V; |
365
|
0
|
|
|
|
|
0
|
my ($minor_version) = $perl_version =~ /^v5\.(\d+)/; |
366
|
0
|
|
|
|
|
0
|
my @new_list = grep { $_ ne $id } @ops_list; |
|
0
|
|
|
|
|
0
|
|
367
|
|
|
|
|
|
|
#say "new_list: @new_list"; |
368
|
0
|
0
|
|
|
|
0
|
if ($minor_version <= 14) |
369
|
0
|
|
|
|
|
0
|
{ $track->{ops} = [ @new_list ] } |
370
|
0
|
|
|
|
|
0
|
else { @{ $track->{ops} } = @new_list } |
|
0
|
|
|
|
|
0
|
|
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
#set_current_op($this_track->ops->[0]); |
373
|
|
|
|
|
|
|
#set_current_param(1); |
374
|
0
|
|
|
|
|
0
|
delete $by_id{$self->id}; |
375
|
0
|
|
|
|
|
0
|
return(); |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
sub position_effect { |
378
|
0
|
|
|
0
|
0
|
0
|
my($self, $pos) = @_; |
379
|
|
|
|
|
|
|
|
380
|
0
|
|
|
|
|
0
|
my $op = $self->id; |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
# disabled, debugging needed |
383
|
|
|
|
|
|
|
# we cannot handle controllers |
384
|
|
|
|
|
|
|
#Audio::Nama::pager("$op or $pos: controller not allowed, skipping.\n"), return |
385
|
|
|
|
|
|
|
# if grep{ fxn($_)->is_controller } $op, $pos; |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
# first, modify track data structure |
388
|
|
|
|
|
|
|
|
389
|
0
|
|
|
|
|
0
|
my $track = $ti{$self->chain}; |
390
|
|
|
|
|
|
|
|
391
|
0
|
|
|
|
|
0
|
my $op_index = $self->track_effect_index; |
392
|
0
|
|
|
|
|
0
|
my @new_op_list = @{$track->ops}; |
|
0
|
|
|
|
|
0
|
|
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
# remove op |
395
|
0
|
|
|
|
|
0
|
splice @new_op_list, $op_index, 1; |
396
|
|
|
|
|
|
|
|
397
|
0
|
0
|
|
|
|
0
|
if ( $pos eq 'ZZZ'){ |
398
|
|
|
|
|
|
|
# put it at the end |
399
|
0
|
|
|
|
|
0
|
push @new_op_list, $op; |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
else { |
402
|
0
|
|
|
|
|
0
|
my $POS = fxn($pos); |
403
|
0
|
|
|
|
|
0
|
my $track2 = $ti{$POS->chain}; |
404
|
0
|
0
|
|
|
|
0
|
Audio::Nama::pager("$pos: position belongs to a different track, skipping.\n"), return |
405
|
|
|
|
|
|
|
unless $track eq $track2; |
406
|
0
|
|
|
|
|
0
|
my $new_op_index = $POS->track_effect_index; |
407
|
|
|
|
|
|
|
# insert op |
408
|
0
|
|
|
|
|
0
|
splice @new_op_list, $new_op_index, 0, $op; |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
# reconfigure the entire engine (inefficient, but easy to do) |
411
|
0
|
|
|
|
|
0
|
say join " - ",@new_op_list; |
412
|
0
|
|
|
|
|
0
|
@{$track->ops} = @new_op_list; |
|
0
|
|
|
|
|
0
|
|
413
|
0
|
|
|
|
|
0
|
Audio::Nama::request_setup(); |
414
|
0
|
|
|
|
|
0
|
$this_track = $track; |
415
|
|
|
|
|
|
|
# this command generates spurious warnings during test |
416
|
0
|
|
|
|
|
0
|
process_command('show_track'); |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
sub apply_op { |
420
|
0
|
|
|
0
|
0
|
0
|
logsub("&apply_op"); |
421
|
0
|
|
|
|
|
0
|
my $self = shift; |
422
|
0
|
|
|
|
|
0
|
local $config->{category} = 'ECI_FX'; |
423
|
0
|
|
|
|
|
0
|
my $id = $self->id; |
424
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug', "id: $id"); |
425
|
0
|
0
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'logcluck', "$id: expected effect entry not found!"), return |
426
|
|
|
|
|
|
|
if effect_entry_is_bad($id); |
427
|
0
|
|
|
|
|
0
|
my $code = $self->type; |
428
|
0
|
|
|
|
|
0
|
my $dad = $self->parent; |
429
|
0
|
|
|
|
|
0
|
my $chain = $self->chain; |
430
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug', "chain: $chain, type: $code"); |
431
|
|
|
|
|
|
|
# if code contains colon, then follow with comma (preset, LADSPA) |
432
|
|
|
|
|
|
|
# if code contains no colon, then follow with colon (ecasound, ctrl) |
433
|
|
|
|
|
|
|
|
434
|
0
|
0
|
|
|
|
0
|
$code = '-' . $code . ($code =~ /:/ ? q(,) : q(:) ); |
435
|
0
|
|
|
|
|
0
|
my @vals = @{ $self->params }; |
|
0
|
|
|
|
|
0
|
|
436
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug', "values: @vals"); |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
# we start to build iam command |
439
|
|
|
|
|
|
|
|
440
|
0
|
0
|
|
|
|
0
|
my $add_cmd = $dad ? "ctrl-add " : "cop-add "; |
441
|
|
|
|
|
|
|
|
442
|
0
|
|
|
|
|
0
|
$add_cmd .= $code . join ",", @vals; |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
# append the -kx operator for a controller-controller |
445
|
0
|
0
|
0
|
|
|
0
|
$add_cmd .= " -kx" if $dad and $dad->is_controller; |
446
|
|
|
|
|
|
|
|
447
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug', "command: $add_cmd"); |
448
|
|
|
|
|
|
|
|
449
|
0
|
|
|
|
|
0
|
Audio::Nama::eval_iam("c-select $chain"); |
450
|
0
|
0
|
|
|
|
0
|
Audio::Nama::eval_iam("cop-select " . $dad->ecasound_effect_index) if $dad; |
451
|
0
|
|
|
|
|
0
|
Audio::Nama::eval_iam($add_cmd); |
452
|
0
|
0
|
|
|
|
0
|
Audio::Nama::eval_iam("cop-bypass on") if $self->bypassed; |
453
|
|
|
|
|
|
|
|
454
|
0
|
|
|
|
|
0
|
my $owns = $self->owns; |
455
|
0
|
0
|
|
|
|
0
|
(ref $owns) =~ /ARRAY/ or croak "expected array"; |
456
|
0
|
|
|
|
|
0
|
logpkg(__FILE__,__LINE__,'debug',"children found: ". join ",", @$owns); |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
#### Effect related routines, some exported, non-OO |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
sub import_engine_subs { |
463
|
|
|
|
|
|
|
|
464
|
1
|
|
|
1
|
0
|
3
|
*valid_engine_setup = \&Audio::Nama::valid_engine_setup; |
465
|
1
|
|
|
|
|
3
|
*engine_running = \&Audio::Nama::engine_running; |
466
|
1
|
|
|
|
|
3
|
*eval_iam = \&Audio::Nama::eval_iam; |
467
|
1
|
|
|
|
|
3
|
*ecasound_select_chain = \&Audio::Nama::ecasound_select_chain; |
468
|
1
|
|
|
|
|
3
|
*sleeper = \&Audio::Nama::sleeper; |
469
|
1
|
|
|
|
|
3
|
*process_command = \&Audio::Nama::process_command; |
470
|
1
|
|
|
|
|
3
|
*pager = \&Audio::Nama::pager; |
471
|
1
|
|
|
|
|
2
|
*this_op = \&Audio::Nama::this_op; |
472
|
1
|
|
|
|
|
2
|
*this_param = \&Audio::Nama::this_param; |
473
|
1
|
|
|
|
|
2
|
*this_stepsize = \&Audio::Nama::this_stepsize; |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
|
476
|
1
|
|
|
1
|
|
6
|
use Exporter qw(import); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
181
|
|
477
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => [ qw( |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
effect_index |
480
|
|
|
|
|
|
|
full_effect_code |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
effect_entry_is_bad |
483
|
|
|
|
|
|
|
check_fx_consistency |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
new_effect_id |
486
|
|
|
|
|
|
|
add_effect |
487
|
|
|
|
|
|
|
_add_effect |
488
|
|
|
|
|
|
|
append_effect |
489
|
|
|
|
|
|
|
remove_effect |
490
|
|
|
|
|
|
|
remove_fader_effect |
491
|
|
|
|
|
|
|
modify_effect |
492
|
|
|
|
|
|
|
modify_multiple_effects |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
_update_effect |
495
|
|
|
|
|
|
|
update_effect |
496
|
|
|
|
|
|
|
sync_effect_parameters |
497
|
|
|
|
|
|
|
find_op_offsets |
498
|
|
|
|
|
|
|
apply_ops |
499
|
|
|
|
|
|
|
expanded_ops_list |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
bypass_effects |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
restore_effects |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
fxn |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
set_current_op |
508
|
|
|
|
|
|
|
set_current_param |
509
|
|
|
|
|
|
|
set_current_stepsize |
510
|
|
|
|
|
|
|
increment_param |
511
|
|
|
|
|
|
|
decrement_param |
512
|
|
|
|
|
|
|
set_parameter_value |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
) ] ); |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
our @EXPORT = (); |
519
|
|
|
|
|
|
|
|
520
|
1
|
|
|
1
|
|
5
|
no warnings 'uninitialized'; # needed to avoid confusing test TAP output |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6213
|
|
521
|
|
|
|
|
|
|
sub effect_entry_is_bad { |
522
|
0
|
|
|
0
|
0
|
|
my $id = shift; |
523
|
|
|
|
|
|
|
! defined $id |
524
|
0
|
|
0
|
|
|
|
or ! $Audio::Nama::Effect::by_id{$id} |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
# make sure the chain number (track index) is set |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
sub set_chain_value { |
530
|
|
|
|
|
|
|
|
531
|
0
|
|
|
0
|
0
|
|
my $p = shift; |
532
|
|
|
|
|
|
|
|
533
|
0
|
0
|
|
|
|
|
return if $p->{chain}; # return if already set |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
# set chain from track if known |
536
|
|
|
|
|
|
|
|
537
|
0
|
0
|
|
|
|
|
if( $p->{track} ) |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
538
|
|
|
|
|
|
|
{ |
539
|
0
|
|
|
|
|
|
$p->{chain} = $p->{track}->n; |
540
|
|
|
|
|
|
|
delete $p->{track} |
541
|
0
|
|
|
|
|
|
} |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
# set chain from parent effect if known (add controller) |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
elsif( $p->{parent_id}) |
546
|
|
|
|
|
|
|
{ |
547
|
0
|
|
|
|
|
|
$p->{chain} = fxn($p->{parent_id})->chain |
548
|
|
|
|
|
|
|
} |
549
|
|
|
|
|
|
|
# set chain from insert target if known (insert effect) |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
elsif( $p->{before} ) |
552
|
|
|
|
|
|
|
{ |
553
|
0
|
|
|
|
|
|
$p->{chain} = fxn($p->{before})->chain; |
554
|
|
|
|
|
|
|
} |
555
|
|
|
|
|
|
|
#logpkg(__FILE__,__LINE__,'debug',(json_out($p)); |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
# How effect chains are added (by default before fader) |
560
|
|
|
|
|
|
|
# user command: add_effect |
561
|
|
|
|
|
|
|
# add_effect(effect_chain => $fxc) calls insert_effect() |
562
|
|
|
|
|
|
|
# insert_effect() |
563
|
|
|
|
|
|
|
# * removes preceding operators |
564
|
|
|
|
|
|
|
# * calls append_effect(effect_chain => $fxc) |
565
|
|
|
|
|
|
|
# + which calls $fxc->add |
566
|
|
|
|
|
|
|
# + which calls append_effect() for each effect |
567
|
|
|
|
|
|
|
# * restores the operators |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
sub add_effect { |
570
|
|
|
|
|
|
|
#logsub('&add_effect'); |
571
|
0
|
|
|
0
|
0
|
|
my $args = shift; |
572
|
0
|
|
|
|
|
|
my $added = _add_effect($args); |
573
|
0
|
|
|
|
|
|
$added->[0]->id |
574
|
|
|
|
|
|
|
} |
575
|
|
|
|
|
|
|
sub _add_effect { |
576
|
0
|
|
|
0
|
|
|
my $p = shift; |
577
|
0
|
|
|
|
|
|
logsub("&_add_effect"); |
578
|
|
|
|
|
|
|
#logpkg(__FILE__,__LINE__,'debug',sub{ "add effect arguments - 0:\n".json_out($p)}); |
579
|
|
|
|
|
|
|
|
580
|
0
|
|
|
|
|
|
set_chain_value($p); |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
### We prohibit creating effects on the Mixdown track |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
### We check $track->forbid_user_ops |
585
|
|
|
|
|
|
|
### which is set on the Mixdown track, |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
### An alternative would be giving each |
588
|
|
|
|
|
|
|
### Track its own add_effect method |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
### For now this is a single case |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
die "user effects forbidden on this track" |
593
|
|
|
|
|
|
|
if $ti{$p->{chain}} |
594
|
|
|
|
|
|
|
and $ti{$p->{chain}}->forbid_user_ops |
595
|
0
|
0
|
0
|
|
|
|
and $p->{type} !~ /$config->{latency_op}/; |
|
|
|
0
|
|
|
|
|
596
|
|
|
|
|
|
|
|
597
|
0
|
|
|
0
|
|
|
logpkg(__FILE__,__LINE__,'debug',sub{ "add effect arguments - 1:\n".json_out($p)}); |
|
0
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
# either insert or add, depending on 'before' setting |
600
|
|
|
|
|
|
|
|
601
|
0
|
0
|
0
|
|
|
|
my $added = (defined $p->{before} and $p->{before} ne 'ZZZ') |
602
|
|
|
|
|
|
|
? insert_effect($p) |
603
|
|
|
|
|
|
|
: append_effect($p); |
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
sub append_effect { |
607
|
0
|
|
|
0
|
0
|
|
my $p = shift; |
608
|
0
|
|
|
|
|
|
logsub("&append_effect",Dumper $p); |
609
|
0
|
|
|
|
|
|
my %args = %$p; |
610
|
0
|
|
0
|
|
|
|
$args{params} //= []; |
611
|
0
|
|
|
|
|
|
my $track = $ti{$args{chain}}; |
612
|
0
|
|
|
|
|
|
my $add_effects_sub; # we will execute this with engine stopped |
613
|
|
|
|
|
|
|
my @added; |
614
|
0
|
0
|
|
|
|
|
if( $args{effect_chain}) |
615
|
|
|
|
|
|
|
{ |
616
|
|
|
|
|
|
|
# we will create and apply the effects later |
617
|
|
|
|
|
|
|
|
618
|
0
|
|
|
0
|
|
|
$add_effects_sub = sub{ $args{effect_chain}->add($track)}; |
|
0
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
} |
620
|
|
|
|
|
|
|
else |
621
|
|
|
|
|
|
|
{ |
622
|
|
|
|
|
|
|
# create the effect now, apply it later |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
# assign defaults if no values supplied |
625
|
0
|
|
|
|
|
|
my $count = $fx_cache->{registry}->[effect_index($args{type})]->{count} ; |
626
|
0
|
|
|
|
|
|
my @defaults = @{fx_defaults($args{type})}; |
|
0
|
|
|
|
|
|
|
627
|
0
|
0
|
|
|
|
|
if( @defaults ) |
628
|
|
|
|
|
|
|
{ |
629
|
0
|
|
|
|
|
|
for my $i (0..$count - 1) |
630
|
|
|
|
|
|
|
{ |
631
|
|
|
|
|
|
|
$args{params}[$i] = $defaults[$i] |
632
|
0
|
0
|
0
|
|
|
|
if ! defined $args{params}[$i] or $args{params}[$i] eq '*' |
633
|
|
|
|
|
|
|
} |
634
|
|
|
|
|
|
|
} |
635
|
0
|
|
|
|
|
|
my $FX = Audio::Nama::Effect->new(%args); |
636
|
0
|
|
|
|
|
|
push @added, $FX; |
637
|
0
|
0
|
|
|
|
|
if( ! $FX->name ) |
638
|
|
|
|
|
|
|
{ |
639
|
0
|
|
|
|
|
|
while( my($alias, $type) = each %{$fx->{alias}} ) |
|
0
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
{ |
641
|
|
|
|
|
|
|
$FX->set_name($track->unique_nickname($alias)), |
642
|
|
|
|
|
|
|
# need to reset 'each' |
643
|
0
|
0
|
|
|
|
|
keys %{$fx->{alias}}, last if $type eq $FX->type |
|
0
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
} |
645
|
|
|
|
|
|
|
} |
646
|
0
|
0
|
|
|
|
|
$ui->add_effect_gui(\%args) unless $track->hide; |
647
|
|
|
|
|
|
|
|
648
|
0
|
|
|
0
|
|
|
$add_effects_sub = sub{ $FX->apply_op }; |
|
0
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
} |
650
|
0
|
0
|
|
|
|
|
if( Audio::Nama::valid_engine_setup() ) |
651
|
|
|
|
|
|
|
{ |
652
|
0
|
0
|
|
|
|
|
if (Audio::Nama::engine_running()) |
653
|
|
|
|
|
|
|
{ |
654
|
0
|
|
|
|
|
|
$track->mute; |
655
|
0
|
|
|
|
|
|
my $result = Audio::Nama::stop_do_start($add_effects_sub, 0.05); |
656
|
0
|
0
|
|
|
|
|
push @added, @$result if is_array($result); |
657
|
0
|
|
|
|
|
|
$track->unmute; |
658
|
|
|
|
|
|
|
} |
659
|
|
|
|
|
|
|
else { |
660
|
0
|
|
|
|
|
|
my $result = $add_effects_sub->(); |
661
|
0
|
0
|
|
|
|
|
push @added, @$result if is_array($result); |
662
|
|
|
|
|
|
|
} |
663
|
|
|
|
|
|
|
} |
664
|
|
|
|
|
|
|
\@added |
665
|
|
|
|
|
|
|
|
666
|
0
|
|
|
|
|
|
} |
667
|
0
|
|
|
0
|
0
|
|
sub is_array { ref $_[0] eq 'ARRAY' } |
668
|
|
|
|
|
|
|
sub insert_effect { |
669
|
0
|
|
|
0
|
0
|
|
my $p = shift; |
670
|
0
|
|
|
|
|
|
logsub("&insert_effect",Dumper $p); |
671
|
0
|
|
|
|
|
|
my %args = %$p; |
672
|
0
|
|
|
|
|
|
local $config->{category} = 'ECI_FX'; |
673
|
0
|
0
|
|
|
|
|
return(append_effect(\%args)) if $args{before} eq 'ZZZ'; |
674
|
0
|
|
|
|
|
|
my $running = Audio::Nama::engine_running(); |
675
|
0
|
0
|
0
|
|
|
|
pager("Cannot insert effect while engine is recording.\n"), return |
676
|
|
|
|
|
|
|
if $running and Audio::Nama::ChainSetup::really_recording(); |
677
|
|
|
|
|
|
|
pager("Cannot insert effect before controller.\n"), return |
678
|
0
|
0
|
|
|
|
|
if fxn($args{before})->is_controller; |
679
|
0
|
0
|
|
|
|
|
if ($running){ |
680
|
0
|
|
|
|
|
|
$ui->stop_heartbeat; |
681
|
0
|
|
|
|
|
|
Audio::Nama::mute(); |
682
|
0
|
|
|
|
|
|
Audio::Nama::stop_command(); |
683
|
0
|
|
|
|
|
|
sleeper( 0.05); |
684
|
|
|
|
|
|
|
} |
685
|
0
|
0
|
|
|
|
|
my $pos = fxn($args{before}) or die "$args{before}: effect ID not found"; |
686
|
0
|
|
|
|
|
|
my $track = $pos->track; |
687
|
0
|
0
|
|
|
|
|
$this_track eq $pos->track or die "$args{before} is not on current track"; |
688
|
|
|
|
|
|
|
# |
689
|
|
|
|
|
|
|
#logpkg(__FILE__,__LINE__,'debug', $track->name, $/; |
690
|
|
|
|
|
|
|
#logpkg(__FILE__,__LINE__,'debug', "@{$track->ops}") |
691
|
|
|
|
|
|
|
|
692
|
0
|
|
|
|
|
|
my $offset = $pos->track_effect_index; |
693
|
0
|
|
|
|
|
|
my $last_index = $#{$track->ops}; |
|
0
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
# note ops after insertion point |
696
|
0
|
|
|
|
|
|
my @after_ops = @{$track->ops}[$offset..$last_index]; |
|
0
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
# remove corresponding chain operators from the engine |
699
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug',"ops to remove and re-apply: @after_ops"); |
700
|
0
|
|
|
|
|
|
my $connected = Audio::Nama::eval_iam('cs-connected'); |
701
|
0
|
0
|
|
|
|
|
if ( $connected ){ |
702
|
0
|
|
|
|
|
|
map{ remove_op($_)} reverse @after_ops; # reverse order for correct index |
|
0
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
} |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
# remove the corresponding ids from the track list |
706
|
0
|
|
|
|
|
|
splice @{$track->ops}, $offset; |
|
0
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
# add the new effect in the proper position |
709
|
0
|
|
|
|
|
|
my $added = append_effect(\%args); |
710
|
|
|
|
|
|
|
|
711
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug',"@{$track->ops}"); |
|
0
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
# replace the effects that had been removed |
714
|
0
|
|
|
|
|
|
push @{$track->ops}, @after_ops; |
|
0
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
|
716
|
0
|
|
|
0
|
|
|
logpkg(__FILE__,__LINE__,'debug',sub{"@{$track->ops}"}); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
# replace the corresponding Ecasound chain operators |
719
|
0
|
0
|
|
|
|
|
if ($connected ){ |
720
|
0
|
|
|
|
|
|
map{ fxn($_)->apply_op } @after_ops; |
|
0
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
} |
722
|
|
|
|
|
|
|
|
723
|
0
|
0
|
|
|
|
|
if ($running){ |
724
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam('start'); |
725
|
0
|
|
|
|
|
|
sleeper(0.3); |
726
|
0
|
|
|
|
|
|
Audio::Nama::unmute(); |
727
|
0
|
|
|
|
|
|
$ui->start_heartbeat; |
728
|
|
|
|
|
|
|
} |
729
|
0
|
|
|
|
|
|
$added; |
730
|
|
|
|
|
|
|
} |
731
|
|
|
|
|
|
|
sub modify_effect { |
732
|
0
|
|
|
0
|
0
|
|
logsub("&modify_effect"); |
733
|
0
|
|
|
|
|
|
my ($op_id, $parameter, $sign, $value) = @_; |
734
|
|
|
|
|
|
|
# $parameter: one-based |
735
|
|
|
|
|
|
|
|
736
|
0
|
0
|
|
|
|
|
my $FX = fxn($op_id) |
737
|
|
|
|
|
|
|
or pager("$op_id: non-existing effect id. Skipping.\n"), return; |
738
|
0
|
|
|
|
|
|
$FX->_modify_effect($parameter, $value, $sign); |
739
|
|
|
|
|
|
|
} |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
sub modify_multiple_effects { |
743
|
0
|
|
|
0
|
0
|
|
logsub("&modify_multiple_effects"); |
744
|
0
|
|
|
|
|
|
my ($op_ids, $parameters, $sign, $value) = @_; |
745
|
0
|
|
|
|
|
|
map{ my $op_id = $_; |
|
0
|
|
|
|
|
|
|
746
|
0
|
|
|
|
|
|
map{ my $parameter = $_; |
|
0
|
|
|
|
|
|
|
747
|
0
|
|
|
|
|
|
modify_effect($op_id, $parameter, $sign, $value); |
748
|
0
|
|
|
|
|
|
set_current_op($op_id); |
749
|
0
|
|
|
|
|
|
set_current_param($parameter); |
750
|
|
|
|
|
|
|
} @$parameters; |
751
|
|
|
|
|
|
|
} @$op_ids; |
752
|
|
|
|
|
|
|
} |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
sub remove_effect { |
755
|
0
|
|
|
0
|
0
|
|
logsub("&remove_effect"); |
756
|
0
|
|
|
|
|
|
my $id = shift; |
757
|
0
|
0
|
|
|
|
|
my $FX = fxn($id) |
758
|
|
|
|
|
|
|
or logpkg(__FILE__,__LINE__,'logcarp',"$id: does not exist, skipping...\n"), return; |
759
|
0
|
|
|
|
|
|
$FX->_remove_effect; |
760
|
|
|
|
|
|
|
} |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
sub full_effect_code { |
763
|
|
|
|
|
|
|
# get text effect code from user input, which could be |
764
|
|
|
|
|
|
|
# - LADSPA Unique ID (number) |
765
|
|
|
|
|
|
|
# - LADSPA Label (el:something) |
766
|
|
|
|
|
|
|
# - abbreviated LADSPA label (something) |
767
|
|
|
|
|
|
|
# - Ecasound operator (something) |
768
|
|
|
|
|
|
|
# - abbreviated Ecasound preset (something) |
769
|
|
|
|
|
|
|
# - Ecasound preset (pn:something) |
770
|
|
|
|
|
|
|
# - user alias |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
# there is no interference in these labels at present, |
773
|
|
|
|
|
|
|
# so we offer the convenience of using them without |
774
|
|
|
|
|
|
|
# el: and pn: prefixes. |
775
|
|
|
|
|
|
|
|
776
|
0
|
|
|
0
|
0
|
|
my $input = shift; |
777
|
0
|
|
|
|
|
|
my $code; |
778
|
0
|
0
|
|
|
|
|
if ($input !~ /\D/) # i.e. $input is all digits |
|
|
0
|
|
|
|
|
|
779
|
|
|
|
|
|
|
{ |
780
|
0
|
|
|
|
|
|
$code = $fx_cache->{ladspa_id_to_label}->{$input}; |
781
|
|
|
|
|
|
|
} |
782
|
|
|
|
|
|
|
elsif ( $fx_cache->{full_label_to_index}->{$input} ) |
783
|
|
|
|
|
|
|
{ |
784
|
0
|
|
|
|
|
|
$code = $input |
785
|
|
|
|
|
|
|
} |
786
|
|
|
|
|
|
|
else |
787
|
|
|
|
|
|
|
{ |
788
|
0
|
|
|
|
|
|
$code = $fx_cache->{partial_label_to_full}->{$input} |
789
|
|
|
|
|
|
|
} |
790
|
0
|
|
|
|
|
|
$code |
791
|
|
|
|
|
|
|
} |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
# get integer effect index for Nama effect registry |
795
|
|
|
|
|
|
|
# e.g. ea => 2 |
796
|
|
|
|
|
|
|
sub effect_index { |
797
|
0
|
|
|
0
|
0
|
|
my $code = shift; |
798
|
0
|
|
|
|
|
|
my $i = $fx_cache->{full_label_to_index}->{full_effect_code($code)}; |
799
|
0
|
0
|
0
|
|
|
|
defined $i or $config->{opts}->{E} or warn("$code: effect index not found\n"); |
800
|
0
|
|
|
|
|
|
$i |
801
|
|
|
|
|
|
|
} |
802
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
sub fx_defaults { |
804
|
0
|
|
|
0
|
0
|
|
my $code = shift; |
805
|
0
|
|
|
|
|
|
my $i = effect_index($code); |
806
|
0
|
|
|
|
|
|
my $values = []; |
807
|
0
|
|
|
|
|
|
foreach my $p ( @{ $fx_cache->{registry}->[$i]->{params} }) |
|
0
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
{ |
809
|
0
|
0
|
|
|
|
|
return [] unless defined $p->{default}; |
810
|
0
|
|
|
|
|
|
push @$values, $p->{default}; |
811
|
|
|
|
|
|
|
} |
812
|
|
|
|
|
|
|
$values |
813
|
0
|
|
|
|
|
|
} |
814
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
## Ecasound engine -- apply/remove chain operators |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
sub apply_ops { # in addition to operators in .ecs file |
819
|
0
|
|
|
0
|
0
|
|
logsub("&apply_ops"); |
820
|
0
|
|
|
|
|
|
for my $track ( Audio::Nama::audio_tracks() ) { |
821
|
0
|
|
|
|
|
|
my $n = $track->n; |
822
|
0
|
0
|
|
|
|
|
next unless Audio::Nama::ChainSetup::is_ecasound_chain($n); |
823
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', "chain: $n, offset: $fx->{offset}->{$n}"); |
824
|
0
|
|
|
|
|
|
$track->apply_ops; |
825
|
|
|
|
|
|
|
} |
826
|
0
|
0
|
|
|
|
|
ecasound_select_chain($this_track->n) if defined $this_track; |
827
|
|
|
|
|
|
|
} |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
sub remove_op { |
830
|
|
|
|
|
|
|
# remove chain operator from Ecasound engine |
831
|
|
|
|
|
|
|
|
832
|
0
|
|
|
0
|
0
|
|
logsub("&remove_op"); |
833
|
0
|
|
|
|
|
|
local $config->{category} = 'ECI_FX'; |
834
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
# only if engine is configured |
836
|
0
|
0
|
|
|
|
|
return unless Audio::Nama::valid_engine_setup(); |
837
|
|
|
|
|
|
|
|
838
|
0
|
|
|
|
|
|
my $id = shift; |
839
|
0
|
|
|
|
|
|
my $self = fxn($id); |
840
|
0
|
|
|
|
|
|
my $n = $self->chain; |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
# select chain |
843
|
|
|
|
|
|
|
|
844
|
0
|
0
|
|
|
|
|
return unless ecasound_select_chain($n); |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
# deal separately with controllers and chain operators |
847
|
|
|
|
|
|
|
|
848
|
0
|
|
|
|
|
|
my $index; |
849
|
|
|
|
|
|
|
|
850
|
0
|
0
|
|
|
|
|
if ( ! $self->is_controller) { # chain operator |
851
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', "no parent, assuming chain operator"); |
852
|
|
|
|
|
|
|
|
853
|
0
|
|
|
|
|
|
$index = $self->ecasound_effect_index; |
854
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', "ops list for chain $n: @{$ti{$n}->ops}"); |
|
0
|
|
|
|
|
|
|
855
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', "operator id to remove: $id"); |
856
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', "ready to remove from chain $n, operator id $id, index $index"); |
857
|
0
|
|
|
0
|
|
|
logpkg(__FILE__,__LINE__,'debug',sub{Audio::Nama::eval_iam("cs")}); |
|
0
|
|
|
|
|
|
|
858
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("cop-select ". $self->ecasound_effect_index); |
859
|
0
|
|
|
0
|
|
|
logpkg(__FILE__,__LINE__,'debug',sub{"selected operator: ". Audio::Nama::eval_iam("cop-selected")}); |
|
0
|
|
|
|
|
|
|
860
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("cop-remove"); |
861
|
0
|
|
|
0
|
|
|
logpkg(__FILE__,__LINE__,'debug',sub{Audio::Nama::eval_iam("cs")}); |
|
0
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
} else { # controller |
864
|
|
|
|
|
|
|
|
865
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', "has parent, assuming controller"); |
866
|
|
|
|
|
|
|
|
867
|
0
|
|
|
|
|
|
my $ctrl_index = $self->ecasound_controller_index; |
868
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', Audio::Nama::eval_iam("cs")); |
869
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("cop-select ". $self->root_parent->ecasound_controller_index); |
870
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', "selected operator: ". Audio::Nama::eval_iam("cop-selected")); |
871
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("ctrl-select $ctrl_index"); |
872
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("ctrl-remove"); |
873
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', Audio::Nama::eval_iam("cs")); |
874
|
|
|
|
|
|
|
} |
875
|
|
|
|
|
|
|
} |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
# Track sax effects: A B C GG HH II D E F |
879
|
|
|
|
|
|
|
# GG HH and II are controllers applied to chain operator C |
880
|
|
|
|
|
|
|
# |
881
|
|
|
|
|
|
|
# to remove controller HH: |
882
|
|
|
|
|
|
|
# |
883
|
|
|
|
|
|
|
# for Ecasound, chain op index = 3, |
884
|
|
|
|
|
|
|
# ctrl index = 2 |
885
|
|
|
|
|
|
|
# = track_effect_index HH - track_effect_index C |
886
|
|
|
|
|
|
|
# |
887
|
|
|
|
|
|
|
# |
888
|
|
|
|
|
|
|
# for Nama, chain op array index 2, |
889
|
|
|
|
|
|
|
# ctrl arrray index = chain op array index + ctrl_index |
890
|
|
|
|
|
|
|
# = effect index - 1 + ctrl_index |
891
|
|
|
|
|
|
|
# |
892
|
|
|
|
|
|
|
# |
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
## Nama effects |
895
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
## have a unique ID from capital letters |
897
|
|
|
|
|
|
|
## IDs are kept in the $track->ops |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
## Rules for allocating IDs |
900
|
|
|
|
|
|
|
## new_effect_id() - issues a new ID |
901
|
|
|
|
|
|
|
## effect_init() - initializes a Nama effect, should be called effect_init() |
902
|
|
|
|
|
|
|
## add_effect |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
sub new_effect_id { |
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
# increment $fx->{id_counter} if necessary |
907
|
|
|
|
|
|
|
# to find an unused effect_id |
908
|
|
|
|
|
|
|
|
909
|
0
|
|
|
0
|
0
|
|
while( fxn($fx->{id_counter})){ $fx->{id_counter}++}; |
|
0
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
$fx->{id_counter} |
911
|
0
|
|
|
|
|
|
} |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
## synchronize Ecasound chain operator parameters |
916
|
|
|
|
|
|
|
# with Nama effect parameter |
917
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
sub _update_effect { |
919
|
0
|
|
|
0
|
|
|
local $config->{category} = 'ECI_FX'; |
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
# update the parameters of the Ecasound chain operator |
922
|
|
|
|
|
|
|
# referred to by a Nama operator_id |
923
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
#logsub("&update_effect"); |
925
|
|
|
|
|
|
|
|
926
|
0
|
0
|
|
|
|
|
return unless Audio::Nama::valid_engine_setup(); |
927
|
|
|
|
|
|
|
#my $es = Audio::Nama::eval_iam("engine-status"); |
928
|
|
|
|
|
|
|
#logpkg(__FILE__,__LINE__,'debug', "engine is $es"); |
929
|
|
|
|
|
|
|
#return if $es !~ /not started|stopped|running/; |
930
|
|
|
|
|
|
|
|
931
|
0
|
|
|
|
|
|
my ($id, $param, $val) = @_; |
932
|
|
|
|
|
|
|
|
933
|
0
|
0
|
|
|
|
|
my $FX = fxn($id) or carp("$id: effect not found. skipping...\n"), return; |
934
|
0
|
|
|
|
|
|
$param++; # so the value at $p[0] is applied to parameter 1 |
935
|
0
|
|
|
|
|
|
my $chain = $FX->chain; |
936
|
0
|
0
|
|
|
|
|
return unless Audio::Nama::ChainSetup::is_ecasound_chain($chain); |
937
|
|
|
|
|
|
|
|
938
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', "chain $chain id $id param $param value $val"); |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
# $param is zero-based. |
941
|
|
|
|
|
|
|
# $FX->params is zero-based. |
942
|
|
|
|
|
|
|
|
943
|
0
|
0
|
|
|
|
|
my $old_chain = Audio::Nama::eval_iam('c-selected') if Audio::Nama::valid_engine_setup(); |
944
|
0
|
|
|
|
|
|
ecasound_select_chain($chain); |
945
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
# update Ecasound's copy of the parameter |
947
|
0
|
0
|
|
|
|
|
if( $FX->is_controller ){ |
948
|
0
|
|
|
|
|
|
my $i = $FX->ecasound_controller_index; |
949
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', "controller $id: track: $chain, index: $i param: $param, value: $val"); |
950
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("ctrl-select $i"); |
951
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("ctrlp-select $param"); |
952
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("ctrlp-set $val"); |
953
|
|
|
|
|
|
|
} |
954
|
|
|
|
|
|
|
else { # is operator |
955
|
0
|
|
|
|
|
|
my $i = $FX->ecasound_operator_index; |
956
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', "operator $id: track $chain, index: $i, offset: ". $FX->offset . " param $param, value $val"); |
957
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("cop-select ". ($FX->offset + $i)); |
958
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("copp-select $param"); |
959
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("copp-set $val"); |
960
|
|
|
|
|
|
|
} |
961
|
0
|
|
|
|
|
|
ecasound_select_chain($old_chain); |
962
|
|
|
|
|
|
|
} |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
# set both Nama effect and Ecasound chain operator |
965
|
|
|
|
|
|
|
# parameters |
966
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
sub update_effect { |
968
|
0
|
|
|
0
|
0
|
|
my ($id, $param, $val) = @_; |
969
|
0
|
|
|
|
|
|
_update_effect( @_ ); |
970
|
0
|
0
|
|
|
|
|
return if ! defined fxn($id); |
971
|
0
|
|
|
|
|
|
fxn($id)->params->[$param] = $val; |
972
|
|
|
|
|
|
|
} |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
sub sync_effect_parameters { |
975
|
0
|
|
|
0
|
0
|
|
local $config->{category} = 'ECI_FX'; |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
# when a controller changes an effect parameter, the |
978
|
|
|
|
|
|
|
# parameter value can differ from Nama's value for that |
979
|
|
|
|
|
|
|
# parameter. |
980
|
|
|
|
|
|
|
# |
981
|
|
|
|
|
|
|
# this routine syncs them in prep for save_state() |
982
|
|
|
|
|
|
|
|
983
|
0
|
0
|
|
|
|
|
return unless Audio::Nama::valid_engine_setup(); |
984
|
0
|
|
|
|
|
|
my $old_chain = Audio::Nama::eval_iam('c-selected'); |
985
|
0
|
|
|
|
|
|
map{ $_->sync_one_effect } grep{ $_ } map{ fxn($_) } ops_with_controller(), ops_with_read_only_params(); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
986
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("c-select $old_chain"); |
987
|
|
|
|
|
|
|
} |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
sub get_ecasound_cop_params { |
992
|
0
|
|
|
0
|
0
|
|
local $config->{category} = 'ECI_FX'; |
993
|
0
|
|
|
|
|
|
my $count = shift; |
994
|
0
|
|
|
|
|
|
my @params; |
995
|
0
|
|
|
|
|
|
for (1..$count){ |
996
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("copp-select $_"); |
997
|
0
|
|
|
|
|
|
push @params, Audio::Nama::eval_iam("copp-get"); |
998
|
|
|
|
|
|
|
} |
999
|
|
|
|
|
|
|
\@params |
1000
|
0
|
|
|
|
|
|
} |
1001
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
sub ops_with_controller { |
1003
|
0
|
|
|
|
|
|
grep{ ! $_->is_controller } |
1004
|
0
|
|
|
|
|
|
grep{ scalar @{$_->owns} } |
|
0
|
|
|
|
|
|
|
1005
|
0
|
|
|
|
|
|
map{ fxn($_) } |
1006
|
0
|
|
|
0
|
0
|
|
map{ @{ $_->ops } } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
Audio::Nama::ChainSetup::engine_tracks(); |
1008
|
|
|
|
|
|
|
} |
1009
|
|
|
|
|
|
|
sub ops_with_read_only_params { |
1010
|
0
|
|
|
|
|
|
grep{ $_->has_read_only_param() } |
1011
|
0
|
|
|
|
|
|
map{ fxn($_) } |
1012
|
0
|
|
|
0
|
0
|
|
map{ @{ $_->ops } } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
Audio::Nama::ChainSetup::engine_tracks(); |
1014
|
|
|
|
|
|
|
} |
1015
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
sub find_op_offsets { |
1018
|
|
|
|
|
|
|
|
1019
|
0
|
|
|
0
|
0
|
|
local $config->{category} = 'ECI_FX'; |
1020
|
0
|
|
|
|
|
|
logsub("&find_op_offsets"); |
1021
|
0
|
|
|
|
|
|
my @op_offsets = grep{ /"\d+"/} split "\n",Audio::Nama::eval_iam("cs"); |
|
0
|
|
|
|
|
|
|
1022
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', join "\n\n",@op_offsets); |
1023
|
0
|
|
|
|
|
|
for my $output (@op_offsets){ |
1024
|
0
|
|
|
|
|
|
my $chain_id; |
1025
|
0
|
|
|
|
|
|
($chain_id) = $output =~ m/Chain "(\w*\d+)"/; |
1026
|
|
|
|
|
|
|
# "print chain_id: $chain_id\n"; |
1027
|
0
|
0
|
|
|
|
|
next if $chain_id =~ m/\D/; # skip id's containing non-digits |
1028
|
|
|
|
|
|
|
# i.e. M1 |
1029
|
0
|
|
|
|
|
|
my $quotes = $output =~ tr/"//; |
1030
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', "offset: $quotes in $output"); |
1031
|
0
|
|
|
|
|
|
$fx->{offset}->{$chain_id} = $quotes/2 - 1; |
1032
|
|
|
|
|
|
|
} |
1033
|
|
|
|
|
|
|
} |
1034
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
sub expanded_ops_list { # including controllers |
1036
|
|
|
|
|
|
|
# we assume existing ops |
1037
|
0
|
|
|
0
|
0
|
|
my @ops_list = @_; |
1038
|
0
|
0
|
|
|
|
|
return () unless @_; |
1039
|
0
|
|
|
|
|
|
my @expanded = (); |
1040
|
|
|
|
|
|
|
map |
1041
|
0
|
|
|
|
|
|
{ push @expanded, |
1042
|
|
|
|
|
|
|
$_, |
1043
|
0
|
|
|
|
|
|
expanded_ops_list( reverse @{fxn($_)->owns} ); |
|
0
|
|
|
|
|
|
|
1044
|
|
|
|
|
|
|
|
1045
|
|
|
|
|
|
|
# we reverse controllers listing so |
1046
|
|
|
|
|
|
|
# the first controller is applied last |
1047
|
|
|
|
|
|
|
# the insert operation places it adjacent to |
1048
|
|
|
|
|
|
|
# its parent controller |
1049
|
|
|
|
|
|
|
# as a result, the controllers end up |
1050
|
|
|
|
|
|
|
# in the same order as the original |
1051
|
|
|
|
|
|
|
# |
1052
|
|
|
|
|
|
|
# which is convenient for RCS |
1053
|
|
|
|
|
|
|
|
1054
|
|
|
|
|
|
|
} @ops_list; |
1055
|
|
|
|
|
|
|
|
1056
|
0
|
|
|
|
|
|
my %seen; |
1057
|
0
|
|
|
|
|
|
@expanded = grep { ! $seen{$_}++ } @expanded; |
|
0
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
} |
1059
|
|
|
|
|
|
|
|
1060
|
|
|
|
|
|
|
sub intersect_with_track_ops_list { |
1061
|
0
|
|
|
0
|
0
|
|
my ($track, @effects) = @_; |
1062
|
0
|
|
|
|
|
|
my %ops; |
1063
|
0
|
|
|
|
|
|
map{ $ops{$_}++} @{$track->ops}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1064
|
0
|
|
|
|
|
|
my @intersection = grep { $ops{$_} } @effects; |
|
0
|
|
|
|
|
|
|
1065
|
0
|
|
|
|
|
|
my @outersection = grep { !$ops{$_} } @effects; |
|
0
|
|
|
|
|
|
|
1066
|
0
|
0
|
|
|
|
|
carp "@outersection: effects don't belong to track: ", $track->name, |
1067
|
|
|
|
|
|
|
". skipping." if @outersection; |
1068
|
|
|
|
|
|
|
@intersection |
1069
|
0
|
|
|
|
|
|
} |
1070
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
sub bypass_effects { |
1072
|
0
|
|
|
0
|
0
|
|
my($track, @ops) = @_; |
1073
|
0
|
|
|
|
|
|
set_bypass_state($track, 'on', @ops); |
1074
|
|
|
|
|
|
|
} |
1075
|
|
|
|
|
|
|
sub restore_effects { |
1076
|
0
|
|
|
0
|
0
|
|
my($track, @ops) = @_; |
1077
|
0
|
|
|
|
|
|
set_bypass_state($track, 'off', @ops); |
1078
|
|
|
|
|
|
|
} |
1079
|
|
|
|
|
|
|
|
1080
|
|
|
|
|
|
|
sub set_bypass_state { |
1081
|
|
|
|
|
|
|
|
1082
|
0
|
|
|
0
|
0
|
|
local $config->{category} = 'ECI_FX'; |
1083
|
0
|
|
|
|
|
|
my($track, $bypass_state, @ops) = @_; |
1084
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
# only process ops that belong to this track |
1086
|
0
|
|
|
|
|
|
@ops = intersect_with_track_ops_list($track,@ops); |
1087
|
|
|
|
|
|
|
|
1088
|
0
|
|
|
|
|
|
$track->mute; |
1089
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("c-select ".$track->n); |
1090
|
|
|
|
|
|
|
|
1091
|
0
|
|
|
|
|
|
foreach my $op ( @ops) |
1092
|
|
|
|
|
|
|
{ |
1093
|
0
|
|
|
|
|
|
my $FX = fxn($op); |
1094
|
0
|
|
|
|
|
|
my $i = $FX->ecasound_effect_index; |
1095
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("cop-select $i"); |
1096
|
0
|
|
|
|
|
|
Audio::Nama::eval_iam("cop-bypass $bypass_state"); |
1097
|
0
|
0
|
|
|
|
|
$FX->set(bypassed => ($bypass_state eq 'on') ? 1 : 0); |
1098
|
|
|
|
|
|
|
} |
1099
|
0
|
|
|
|
|
|
$track->unmute; |
1100
|
|
|
|
|
|
|
} |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
sub remove_fader_effect { |
1103
|
0
|
|
|
0
|
0
|
|
my ($track, $role) = @_; |
1104
|
0
|
|
|
|
|
|
remove_effect($track->$role); |
1105
|
0
|
|
|
|
|
|
delete $track->{$role} |
1106
|
|
|
|
|
|
|
} |
1107
|
|
|
|
|
|
|
# Object interface for effects |
1108
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
sub fxn { |
1110
|
0
|
|
|
0
|
0
|
|
my $id = shift; |
1111
|
0
|
|
|
|
|
|
$by_id{$id}; |
1112
|
|
|
|
|
|
|
} |
1113
|
|
|
|
|
|
|
sub set_current_op { |
1114
|
0
|
|
|
0
|
0
|
|
my $op_id = shift; |
1115
|
0
|
|
|
|
|
|
my $FX = fxn($op_id); |
1116
|
0
|
0
|
|
|
|
|
return unless $FX; |
1117
|
0
|
|
|
|
|
|
my $track = $ti{$FX->chain}; |
1118
|
0
|
|
|
|
|
|
$project->{current_op}->{$track->name} = $op_id; |
1119
|
|
|
|
|
|
|
} |
1120
|
|
|
|
|
|
|
sub set_current_param { |
1121
|
0
|
|
|
0
|
0
|
|
my $parameter = shift; |
1122
|
0
|
|
|
|
|
|
$project->{current_param}->{Audio::Nama::this_op()} = $parameter; |
1123
|
|
|
|
|
|
|
} |
1124
|
|
|
|
|
|
|
sub set_current_stepsize { |
1125
|
0
|
|
|
0
|
0
|
|
my $stepsize = shift; |
1126
|
0
|
|
|
|
|
|
$project->{current_stepsize}->{Audio::Nama::this_op()}->[this_param()] = $stepsize; |
1127
|
|
|
|
|
|
|
} |
1128
|
0
|
|
|
0
|
0
|
|
sub increment_param { modify_effect(Audio::Nama::this_op(), this_param(),'+',this_stepsize())} |
1129
|
0
|
|
|
0
|
0
|
|
sub decrement_param { modify_effect(Audio::Nama::this_op(), this_param(),'-',this_stepsize())} |
1130
|
|
|
|
|
|
|
sub set_parameter_value { |
1131
|
0
|
|
|
0
|
0
|
|
my $value = shift; |
1132
|
0
|
|
|
|
|
|
modify_effect(Audio::Nama::this_op(), this_param(), undef, $value) |
1133
|
|
|
|
|
|
|
} |
1134
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
|
1136
|
|
|
|
|
|
|
sub check_fx_consistency { |
1137
|
|
|
|
|
|
|
|
1138
|
0
|
|
|
0
|
0
|
|
my $result = {}; |
1139
|
0
|
|
|
|
|
|
my %seen_ids; |
1140
|
|
|
|
|
|
|
my $is_error; |
1141
|
|
|
|
|
|
|
map |
1142
|
|
|
|
|
|
|
{ |
1143
|
0
|
|
|
|
|
|
my $track = $_; |
|
0
|
|
|
|
|
|
|
1144
|
0
|
|
|
|
|
|
my $name = $track->name; |
1145
|
0
|
|
|
|
|
|
my @ops = @{ $track->{ops} }; |
|
0
|
|
|
|
|
|
|
1146
|
0
|
|
|
|
|
|
my $is_track_error; |
1147
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
# check for missing special-purpose ops |
1149
|
|
|
|
|
|
|
|
1150
|
0
|
|
|
|
|
|
my $no_vol_op = ! $track->vol; |
1151
|
0
|
|
|
|
|
|
my $no_pan_op = ! $track->pan; |
1152
|
0
|
|
|
|
|
|
my $no_latency_op = ! $track->latency_op; |
1153
|
|
|
|
|
|
|
|
1154
|
|
|
|
|
|
|
# check for orphan special-purpose op entries |
1155
|
|
|
|
|
|
|
|
1156
|
|
|
|
|
|
|
$is_track_error++, $result->{track}->{$name}->{orphan_vol} = $track->vol |
1157
|
0
|
0
|
0
|
|
|
|
if $track->vol and ! grep { $track->vol eq $_ } @ops; |
|
0
|
|
|
|
|
|
|
1158
|
|
|
|
|
|
|
$is_track_error++,$result->{track}->{$name}->{orphan_pan} = $track->pan |
1159
|
0
|
0
|
0
|
|
|
|
if $track->pan and ! grep { $track->pan eq $_ } @ops; |
|
0
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
|
1161
|
|
|
|
|
|
|
# we don't check for orphan latency ops as this is |
1162
|
|
|
|
|
|
|
# allowed in order to keep constant $op_id over |
1163
|
|
|
|
|
|
|
# time (slower incrementing of fx counter) |
1164
|
|
|
|
|
|
|
|
1165
|
|
|
|
|
|
|
#$is_track_error++,$result->{track}->{$name}->{orphan_latency_op} = $track->latency_op |
1166
|
|
|
|
|
|
|
# if $track->latency_op and ! grep { $track->latency_op eq $_ } @ops; |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
# check for undefined op ids |
1169
|
|
|
|
|
|
|
|
1170
|
0
|
|
|
|
|
|
my @track_undef_op_pos; |
1171
|
|
|
|
|
|
|
|
1172
|
0
|
|
|
|
|
|
my $i = 0; |
1173
|
0
|
0
|
|
|
|
|
map { defined $_ or push @track_undef_op_pos, $i; $i++ } @ops; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
$is_track_error++,$result->{track}->{$name}->{undef_op_pos} |
1175
|
0
|
0
|
|
|
|
|
= \@track_undef_op_pos if @track_undef_op_pos; |
1176
|
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
# remove undefined op ids from list |
1178
|
|
|
|
|
|
|
|
1179
|
0
|
|
|
|
|
|
@ops = grep{ $_ } @ops; |
|
0
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
|
1181
|
|
|
|
|
|
|
# check for op ids without corresponding entry |
1182
|
|
|
|
|
|
|
|
1183
|
0
|
|
|
|
|
|
my @uninstantiated_op_ids; |
1184
|
0
|
0
|
|
|
|
|
map { fxn($_) or push @uninstantiated_op_ids, $_ } @ops; |
|
0
|
|
|
|
|
|
|
1185
|
|
|
|
|
|
|
|
1186
|
|
|
|
|
|
|
$is_track_error++, $result->{track}->{$name}->{uninstantiated_op_ids} |
1187
|
0
|
0
|
|
|
|
|
= \@uninstantiated_op_ids if @uninstantiated_op_ids; |
1188
|
|
|
|
|
|
|
|
1189
|
0
|
0
|
|
|
|
|
$result->{track}->{$name}->{is_error}++ if $is_track_error; |
1190
|
0
|
0
|
|
|
|
|
$result->{is_error}++ if $is_track_error; |
1191
|
|
|
|
|
|
|
} Audio::Nama::audio_tracks(); |
1192
|
|
|
|
|
|
|
|
1193
|
|
|
|
|
|
|
# check for objects missing fields |
1194
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
my @incomplete_entries = |
1196
|
0
|
|
0
|
|
|
|
grep { ! fxn($_)->params or ! fxn($_)->type or ! fxn($_)->chain } |
1197
|
0
|
|
|
|
|
|
grep { $_ } keys %Audio::Nama::Effect::by_id; |
|
0
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
|
1199
|
0
|
0
|
|
|
|
|
if(@incomplete_entries) |
1200
|
|
|
|
|
|
|
{ |
1201
|
0
|
|
|
|
|
|
$result->{incomplete_entries} = \@incomplete_entries; |
1202
|
0
|
|
|
|
|
|
$result->{is_error}++ |
1203
|
|
|
|
|
|
|
} |
1204
|
0
|
|
|
|
|
|
$result; |
1205
|
|
|
|
|
|
|
} |
1206
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
sub fade { |
1208
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1209
|
|
|
|
|
|
|
# parameter starts at one |
1210
|
0
|
|
|
|
|
|
my ($param, $from, $to, $seconds) = @_; |
1211
|
|
|
|
|
|
|
|
1212
|
0
|
|
|
|
|
|
my $id = $self->id; |
1213
|
|
|
|
|
|
|
# no fade without Timer::HiRes |
1214
|
|
|
|
|
|
|
# no fade unless engine is running |
1215
|
0
|
0
|
0
|
|
|
|
if ( engine_running() and $config->{hires_timer} ) |
1216
|
|
|
|
|
|
|
{ |
1217
|
0
|
|
|
|
|
|
my $steps = $seconds * $config->{fade_resolution}; |
1218
|
0
|
|
|
|
|
|
my $wink = 1/$config->{fade_resolution}; |
1219
|
0
|
|
|
|
|
|
my $size = ($to - $from)/$steps; |
1220
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug', "id: $id, param: $param, from: $from, to: $to, seconds: $seconds"); |
1221
|
|
|
|
|
|
|
# first step by step |
1222
|
0
|
|
|
|
|
|
for (1..$steps - 1){ |
1223
|
0
|
|
|
|
|
|
$self->_modify_effect($param, $size, '+'); |
1224
|
0
|
|
|
|
|
|
sleeper( $wink ); |
1225
|
|
|
|
|
|
|
} |
1226
|
|
|
|
|
|
|
} |
1227
|
0
|
|
|
|
|
|
$self->_modify_effect($param, $to) |
1228
|
|
|
|
|
|
|
} |
1229
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
sub fadein { |
1231
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1232
|
0
|
|
|
|
|
|
my $to = shift; |
1233
|
0
|
|
|
|
|
|
my $from = $config->{fade_out_level}->{$self->type}; |
1234
|
0
|
|
|
|
|
|
$self->_modify_effect(1, $from); |
1235
|
0
|
|
|
|
|
|
$self->fade(1, $from, $to, $config->{engine_fade_length_on_start_stop}); |
1236
|
|
|
|
|
|
|
} |
1237
|
|
|
|
|
|
|
sub fadeout { |
1238
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1239
|
0
|
|
|
|
|
|
my $from = $self->params->[0]; |
1240
|
0
|
|
|
|
|
|
my $to = $config->{fade_out_level}->{$self->type}; |
1241
|
0
|
|
|
|
|
|
$self->fade(1, $from, $to, $config->{engine_fade_length_on_start_stop} ); |
1242
|
0
|
|
|
|
|
|
$self->_modify_effect(1, $config->{mute_level}->{$self->type}); |
1243
|
|
|
|
|
|
|
} |
1244
|
|
|
|
|
|
|
sub mute_level { |
1245
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1246
|
0
|
|
|
|
|
|
my $level = $config->{mute_level}->{$self->type}; |
1247
|
|
|
|
|
|
|
#defined $level or die $self->nameline . " cannot be muted." |
1248
|
0
|
|
|
|
|
|
$level |
1249
|
|
|
|
|
|
|
} |
1250
|
|
|
|
|
|
|
sub fade_out_level { |
1251
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1252
|
0
|
|
|
|
|
|
$config->{fade_out_level}->{$self->type} |
1253
|
|
|
|
|
|
|
} |
1254
|
|
|
|
|
|
|
|
1255
|
|
|
|
|
|
|
} # end package Effect |
1256
|
|
|
|
|
|
|
|
1257
|
|
|
|
|
|
|
1 |