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