line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# ------------- Effect-Chain and -Profile routines -------- |
2
|
|
|
|
|
|
|
# Effect Chains |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# we have two type of effect chains |
5
|
|
|
|
|
|
|
# + global effect chains - usually user defined, available to all projects |
6
|
|
|
|
|
|
|
# + system generated effect chains, per project |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
{ |
9
|
|
|
|
|
|
|
package Audio::Nama::EffectChain; |
10
|
1
|
|
|
1
|
|
6
|
use Modern::Perl; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
7
|
|
11
|
1
|
|
|
1
|
|
112
|
use Data::Dumper::Concise; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
61
|
|
12
|
1
|
|
|
1
|
|
5
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
59
|
|
13
|
1
|
|
|
1
|
|
5
|
use Exporter qw(import); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
88
|
|
14
|
1
|
|
|
1
|
|
12
|
use Storable qw(dclone); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
57
|
|
15
|
1
|
|
|
1
|
|
6
|
use Audio::Nama::Effect qw(fxn append_effect); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
50
|
|
16
|
1
|
|
|
1
|
|
5
|
use Audio::Nama::Log qw(logpkg logsub); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
50
|
|
17
|
1
|
|
|
1
|
|
5
|
use Audio::Nama::Assign qw(json_out); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
49
|
|
18
|
|
|
|
|
|
|
|
19
|
1
|
|
|
1
|
|
5
|
use Audio::Nama::Globals qw($fx_cache %tn $fx); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
172
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
our $AUTOLOAD; |
22
|
|
|
|
|
|
|
our $VERSION = 0.001; |
23
|
1
|
|
|
1
|
|
6
|
no warnings qw(uninitialized); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
87
|
|
24
|
|
|
|
|
|
|
our @ISA; |
25
|
|
|
|
|
|
|
our ($n, %by_index, @attributes, %is_attribute); |
26
|
1
|
|
|
|
|
7
|
use Audio::Nama::Object qw( |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
n |
29
|
|
|
|
|
|
|
ops_list |
30
|
|
|
|
|
|
|
ops_data |
31
|
|
|
|
|
|
|
inserts_data |
32
|
|
|
|
|
|
|
region |
33
|
|
|
|
|
|
|
attrib |
34
|
|
|
|
|
|
|
class |
35
|
|
|
|
|
|
|
|
36
|
1
|
|
|
1
|
|
5
|
); |
|
1
|
|
|
|
|
3
|
|
37
|
|
|
|
|
|
|
@attributes = qw( |
38
|
|
|
|
|
|
|
name |
39
|
|
|
|
|
|
|
bypass |
40
|
|
|
|
|
|
|
id |
41
|
|
|
|
|
|
|
project |
42
|
|
|
|
|
|
|
global |
43
|
|
|
|
|
|
|
profile |
44
|
|
|
|
|
|
|
user |
45
|
|
|
|
|
|
|
system |
46
|
|
|
|
|
|
|
track_name |
47
|
|
|
|
|
|
|
track_version_result |
48
|
|
|
|
|
|
|
track_version_original |
49
|
|
|
|
|
|
|
track_target_original |
50
|
|
|
|
|
|
|
insert |
51
|
|
|
|
|
|
|
track_cache |
52
|
|
|
|
|
|
|
track_target |
53
|
|
|
|
|
|
|
) ; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
### attributes for searching, sorting, used by external functions |
56
|
|
|
|
|
|
|
# name # for user-defined effect chains |
57
|
|
|
|
|
|
|
# |
58
|
|
|
|
|
|
|
# bypass # used for identifying effect bypass (obsolete) |
59
|
|
|
|
|
|
|
# id # effect id, for storing single effect with controllers |
60
|
|
|
|
|
|
|
# # for bypass (probably obsolete) |
61
|
|
|
|
|
|
|
# |
62
|
|
|
|
|
|
|
# project # true value identifies project-specific effect chain |
63
|
|
|
|
|
|
|
# |
64
|
|
|
|
|
|
|
# global # true value identified global effect chain, |
65
|
|
|
|
|
|
|
# # not project specific, usually user-defined |
66
|
|
|
|
|
|
|
# |
67
|
|
|
|
|
|
|
# profile # name of associated effect profile |
68
|
|
|
|
|
|
|
# |
69
|
|
|
|
|
|
|
# user # true value identifies user created effect chain |
70
|
|
|
|
|
|
|
# |
71
|
|
|
|
|
|
|
# system # true value identifies system generated effect chain |
72
|
|
|
|
|
|
|
# |
73
|
|
|
|
|
|
|
# track_name # applies to a track of this name |
74
|
|
|
|
|
|
|
# |
75
|
|
|
|
|
|
|
# track_version_result # WAV version of track after caching |
76
|
|
|
|
|
|
|
# |
77
|
|
|
|
|
|
|
# track_version_original # WAV version of track before caching |
78
|
|
|
|
|
|
|
# |
79
|
|
|
|
|
|
|
# insert # true value identifies belonging to an insert |
80
|
|
|
|
|
|
|
# |
81
|
|
|
|
|
|
|
# track_cache # true value identifies belonging to track caching |
82
|
|
|
|
|
|
|
# |
83
|
|
|
|
|
|
|
# track_target_original # WAV files were from this track |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
%is_attribute = map{ $_ => 1 } @attributes; |
86
|
|
|
|
|
|
|
initialize(); |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
## sugar for accessing individual effect attributes |
89
|
|
|
|
|
|
|
## similar sugar is used for effects. |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
sub is_controller { |
92
|
0
|
|
|
0
|
0
|
0
|
my ($self, $id) = @_; |
93
|
|
|
|
|
|
|
$self->{ops_data}->{$id}->{belongs_to} |
94
|
0
|
|
|
|
|
0
|
} |
95
|
|
|
|
|
|
|
sub parent_id : lvalue { |
96
|
0
|
|
|
0
|
0
|
0
|
my ($self, $id) = @_; |
97
|
|
|
|
|
|
|
$self->{ops_data}->{$id}->{belongs_to} |
98
|
0
|
|
|
|
|
0
|
} |
99
|
|
|
|
|
|
|
sub type { |
100
|
0
|
|
|
0
|
0
|
0
|
my ($self, $id) = @_; |
101
|
|
|
|
|
|
|
$self->{ops_data}->{$id}->{type} |
102
|
0
|
|
|
|
|
0
|
} |
103
|
|
|
|
|
|
|
sub params { |
104
|
0
|
|
|
0
|
0
|
0
|
my ($self, $id) = @_; |
105
|
|
|
|
|
|
|
$self->{ops_data}->{$id}->{params} |
106
|
0
|
|
|
|
|
0
|
} |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
sub initialize { |
109
|
1
|
|
|
1
|
0
|
3
|
$n = 0; |
110
|
1
|
|
|
|
|
3
|
%by_index = (); |
111
|
|
|
|
|
|
|
} |
112
|
0
|
0
|
|
0
|
0
|
|
sub new_index { $n++; $by_index{$n} ? new_index() : $n } |
|
0
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
sub new { |
114
|
|
|
|
|
|
|
# arguments: ops_list, ops_data, inserts_data |
115
|
|
|
|
|
|
|
# ops_list => [id1, id2, id3,...]; |
116
|
0
|
|
|
0
|
0
|
|
my $class = shift; |
117
|
0
|
|
|
|
|
|
my %vals = @_; |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
# we need to so some preparation if we are creating |
120
|
|
|
|
|
|
|
# an effect chain for the first time (as opposed |
121
|
|
|
|
|
|
|
# to restoring a serialized effect chain) |
122
|
|
|
|
|
|
|
|
123
|
0
|
0
|
|
|
|
|
if (! $vals{n} ) { |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# move secondary attributes to $self->{attrib}->{...} |
126
|
0
|
|
|
|
|
|
move_attributes(\%vals); |
127
|
|
|
|
|
|
|
|
128
|
0
|
|
|
|
|
|
$vals{n} = new_index(); |
129
|
0
|
|
0
|
|
|
|
$vals{inserts_data} ||= []; |
130
|
0
|
|
0
|
|
|
|
$vals{ops_list} ||= []; |
131
|
0
|
|
0
|
|
|
|
$vals{ops_data} ||= {}; |
132
|
0
|
0
|
|
|
|
|
croak "undeclared field in: @_" if grep{ ! $_is_field{$_} } keys %vals; |
|
0
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
croak "must have exactly one of 'global' or 'project' fields defined" |
134
|
0
|
0
|
0
|
|
|
|
unless ($vals{attrib}{global} xor $vals{attrib}{project}); |
135
|
|
|
|
|
|
|
|
136
|
0
|
|
|
0
|
|
|
logpkg(__FILE__,__LINE__,'debug','constructor arguments ', sub{ json_out(\%vals) }); |
|
0
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# we expect some effects |
139
|
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'warn',"Nether ops_list or nor insert_data is present") |
140
|
0
|
0
|
0
|
|
|
|
if ! scalar @{$vals{ops_list}} and ! scalar @{$vals{inserts_data}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
|
142
|
0
|
|
|
|
|
|
my $ops_data = {}; |
143
|
|
|
|
|
|
|
# ops data is taken preferentially |
144
|
|
|
|
|
|
|
# from ops_data argument, with fallback |
145
|
|
|
|
|
|
|
# to existing effects |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# in both cases, we clone the data structures |
148
|
|
|
|
|
|
|
# to ensure we don't damage the original |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
map { |
151
|
|
|
|
|
|
|
|
152
|
0
|
0
|
|
|
|
|
if ( $vals{ops_data}->{$_} ) |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
{ |
155
|
0
|
|
|
|
|
|
$ops_data->{$_} = dclone($vals{ops_data}->{$_}); |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
else |
158
|
|
|
|
|
|
|
{ |
159
|
0
|
|
|
|
|
|
my $filtered_op_data = dclone( fxn($_)->as_hash );# copy |
160
|
0
|
|
|
|
|
|
my @unwanted_keys = qw( chain bypassed name surname display); |
161
|
0
|
|
|
|
|
|
delete $filtered_op_data->{$_} for @unwanted_keys; |
162
|
0
|
|
|
|
|
|
$ops_data->{$_} = $filtered_op_data; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
0
|
|
|
|
|
|
} @{$vals{ops_list}}; |
|
0
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
|
168
|
0
|
|
|
|
|
|
$vals{ops_data} = $ops_data; |
169
|
|
|
|
|
|
|
|
170
|
0
|
0
|
|
|
|
|
if( scalar @{$vals{inserts_data}}) |
|
0
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
{ |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
# rewrite inserts to store what we need: |
174
|
|
|
|
|
|
|
# 1. for general-purpose effects chain use |
175
|
|
|
|
|
|
|
# 2. for track caching use |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
$vals{inserts_data} = |
179
|
|
|
|
|
|
|
[ |
180
|
|
|
|
|
|
|
map |
181
|
|
|
|
|
|
|
{ |
182
|
0
|
|
|
0
|
|
|
logpkg(__FILE__,__LINE__,'debug',"insert: ", sub{Dumper $_}); |
|
0
|
|
|
|
|
|
|
183
|
0
|
|
|
|
|
|
my @wet_ops = @{$tn{$_->wet_name}->ops}; |
|
0
|
|
|
|
|
|
|
184
|
0
|
|
|
|
|
|
my @dry_ops = @{$tn{$_->dry_name}->ops}; |
|
0
|
|
|
|
|
|
|
185
|
0
|
|
|
|
|
|
my $wet_effect_chain = Audio::Nama::EffectChain->new( |
186
|
|
|
|
|
|
|
project => 1, |
187
|
|
|
|
|
|
|
insert => 1, |
188
|
|
|
|
|
|
|
ops_list => \@wet_ops, |
189
|
|
|
|
|
|
|
); |
190
|
0
|
|
|
|
|
|
my $dry_effect_chain = Audio::Nama::EffectChain->new( |
191
|
|
|
|
|
|
|
project => 1, |
192
|
|
|
|
|
|
|
insert => 1, |
193
|
|
|
|
|
|
|
ops_list => \@dry_ops, |
194
|
|
|
|
|
|
|
); |
195
|
0
|
|
|
|
|
|
my $hash = dclone($_->as_hash); |
196
|
|
|
|
|
|
|
|
197
|
0
|
|
|
|
|
|
$hash->{wet_effect_chain} = $wet_effect_chain->n; |
198
|
0
|
|
|
|
|
|
$hash->{dry_effect_chain} = $dry_effect_chain->n; |
199
|
|
|
|
|
|
|
|
200
|
0
|
|
|
|
|
|
map{ delete $hash->{$_} } qw(n dry_vol wet_vol track); |
|
0
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
# Reasons for deleting insert attributes |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# n: we'll get a new index when we re-apply |
205
|
|
|
|
|
|
|
# dry_vol, wet_vol: will never be re-allocated |
206
|
|
|
|
|
|
|
# so why not reuse them? |
207
|
|
|
|
|
|
|
# except for general purpose we'd like to |
208
|
|
|
|
|
|
|
# re-allocate |
209
|
|
|
|
|
|
|
# track: we already know the track from |
210
|
|
|
|
|
|
|
# the parent effect chain |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
# What is left: |
213
|
|
|
|
|
|
|
# |
214
|
|
|
|
|
|
|
# class |
215
|
|
|
|
|
|
|
# wetness |
216
|
|
|
|
|
|
|
# send_type |
217
|
|
|
|
|
|
|
# send_id |
218
|
|
|
|
|
|
|
# return_type |
219
|
|
|
|
|
|
|
# return_id |
220
|
|
|
|
|
|
|
# wet_effect_chain => ec_index, |
221
|
|
|
|
|
|
|
# dry_effect_chain => ec_index, |
222
|
|
|
|
|
|
|
|
223
|
0
|
|
|
|
|
|
$hash |
224
|
0
|
|
|
|
|
|
} @{$vals{inserts_data}} |
|
0
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
]; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
#say Audio::Nama::json_out($vals{inserts_data}) if $vals{inserts_data}; |
229
|
|
|
|
|
|
|
} |
230
|
0
|
|
|
|
|
|
my $object = bless { %vals }, $class; |
231
|
0
|
|
|
|
|
|
$by_index{$vals{n}} = $object; |
232
|
0
|
|
|
0
|
|
|
logpkg(__FILE__,__LINE__,'debug',sub{$object->dump}); |
|
0
|
|
|
|
|
|
|
233
|
0
|
|
|
|
|
|
$object; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
sub AUTOLOAD { |
236
|
0
|
|
|
0
|
|
|
my $self = shift; |
237
|
0
|
|
|
|
|
|
my ($call) = $AUTOLOAD =~ /([^:]+)$/; |
238
|
|
|
|
|
|
|
return $self->{attrib}->{$call} if exists $self->{attrib}->{$call} |
239
|
0
|
0
|
0
|
|
|
|
or $is_attribute{$call}; |
240
|
0
|
|
|
|
|
|
croak "Autoload fell through. Object type: ", (ref $self), ", illegal method call: $call\n"; |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
### apply effect chain to the specified track |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
sub add_ops { |
246
|
0
|
|
|
0
|
0
|
|
my($self, $track, $ec_args) = @_; |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# Higher priority: track argument |
249
|
|
|
|
|
|
|
# Lower priority: effect chain's own track name attribute |
250
|
0
|
0
|
0
|
|
|
|
$track ||= $tn{$self->track_name} if $tn{$self->track_name}; |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
# make sure surname is unique |
254
|
|
|
|
|
|
|
|
255
|
0
|
|
|
|
|
|
my ($new_surname, $existing) = $track->unique_surname($ec_args->{surname}); |
256
|
0
|
0
|
|
|
|
|
if ( $new_surname ne $ec_args->{surname}) |
257
|
|
|
|
|
|
|
{ |
258
|
0
|
|
|
|
|
|
Audio::Nama::pager_newline( |
259
|
|
|
|
|
|
|
"track ". |
260
|
|
|
|
|
|
|
$track->name.qq(: other effects with surname "$ec_args->{surname}" found,), |
261
|
|
|
|
|
|
|
qq(using "$new_surname". Others are: $existing.)); |
262
|
0
|
|
|
|
|
|
$ec_args->{surname} = $new_surname; |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
|
266
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug',$track->name, |
267
|
|
|
|
|
|
|
qq(: adding effect chain ), $self->name, Dumper $self |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
); |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# Exclude restoring vol/pan for track_caching. |
272
|
|
|
|
|
|
|
# (This conditional is a hack that would be better |
273
|
|
|
|
|
|
|
# implemented by subclassing EffectChain |
274
|
|
|
|
|
|
|
# for cache/uncache) |
275
|
|
|
|
|
|
|
|
276
|
0
|
|
|
|
|
|
my @ops_list; |
277
|
|
|
|
|
|
|
my @added; |
278
|
0
|
0
|
|
|
|
|
if( $self->track_cache ){ |
279
|
0
|
0
|
|
|
|
|
@ops_list = grep{ $_ ne $track->vol and $_ ne $track->pan } |
280
|
0
|
|
|
|
|
|
@{$self->ops_list} |
|
0
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
} else { |
282
|
0
|
|
|
|
|
|
@ops_list = @{$self->ops_list}; |
|
0
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
map |
285
|
|
|
|
|
|
|
{ |
286
|
0
|
|
|
|
|
|
my $args = |
|
0
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
{ |
288
|
|
|
|
|
|
|
chain => $track->n, |
289
|
|
|
|
|
|
|
type => $self->type($_), |
290
|
|
|
|
|
|
|
params => $self->params($_), |
291
|
|
|
|
|
|
|
parent => $self->parent_id($_), |
292
|
|
|
|
|
|
|
}; |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
# drop the ID if it is already used |
296
|
0
|
0
|
|
|
|
|
$args->{id} = $_ unless fxn($_); |
297
|
|
|
|
|
|
|
|
298
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug',"args ", json_out($args)); |
299
|
|
|
|
|
|
|
|
300
|
0
|
0
|
|
|
|
|
$args->{surname} = $ec_args->{surname} if $ec_args->{surname}; |
301
|
|
|
|
|
|
|
|
302
|
0
|
|
|
|
|
|
my $FX = append_effect($args)->[0]; |
303
|
0
|
|
|
|
|
|
push @added, $FX; |
304
|
0
|
|
|
|
|
|
my $new_id = $FX->id; |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
# the effect ID may be new, or it may be previously |
307
|
|
|
|
|
|
|
# assigned ID, |
308
|
|
|
|
|
|
|
# whatever value is supplied is guaranteed |
309
|
|
|
|
|
|
|
# to be unique; not to collide with any other effect |
310
|
|
|
|
|
|
|
|
311
|
0
|
|
|
|
|
|
logpkg(__FILE__,__LINE__,'debug',"new id: $new_id"); |
312
|
0
|
|
|
|
|
|
my $orig_id = $_; |
313
|
0
|
0
|
|
|
|
|
if ( $new_id ne $orig_id) |
314
|
|
|
|
|
|
|
# re-write all controllers to belong to new id |
315
|
|
|
|
|
|
|
{ |
316
|
0
|
|
|
|
|
|
map{ $self->parent_id($_) =~ s/^$orig_id$/$new_id/ } @{$self->ops_list} |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
} @ops_list; |
320
|
|
|
|
|
|
|
\@added |
321
|
0
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
sub add_inserts { |
323
|
0
|
|
|
0
|
0
|
|
my ($self, $track) = @_; |
324
|
|
|
|
|
|
|
map |
325
|
|
|
|
|
|
|
{ |
326
|
0
|
|
|
|
|
|
my $insert_data = dclone($_); # copy so safe to modify |
327
|
|
|
|
|
|
|
#say "found insert data:\n",Audio::Nama::json_out($insert_data); |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
# get effect chain indices for wet/dry arms |
330
|
|
|
|
|
|
|
|
331
|
0
|
|
|
|
|
|
my $wet_effect_chain = delete $insert_data->{wet_effect_chain}; |
332
|
0
|
|
|
|
|
|
my $dry_effect_chain = delete $insert_data->{dry_effect_chain}; |
333
|
0
|
|
|
|
|
|
my $class = delete $insert_data->{class}; |
334
|
|
|
|
|
|
|
|
335
|
0
|
|
|
|
|
|
$insert_data->{track} = $track->name; |
336
|
0
|
|
|
|
|
|
my $insert = $class->new(%$insert_data); |
337
|
|
|
|
|
|
|
#$Audio::Nama::by_index{$wet_effect_chain}->add($insert->wet_name, $tn{$insert->wet_name}->vol) |
338
|
|
|
|
|
|
|
#$Audio::Nama::by_index{$dry_effect_chain}->add($insert->dry_name, $tn{$insert->dry_name}->vol) |
339
|
0
|
|
|
|
|
|
} @{$self->inserts_data}; |
|
0
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
sub add_region { |
342
|
0
|
|
|
0
|
0
|
|
my ($self, $track) = @_; |
343
|
|
|
|
|
|
|
# there is also a check in uncache track |
344
|
0
|
0
|
|
|
|
|
Audio::Nama::throw($track->name.": track already has region definition\n", |
345
|
|
|
|
|
|
|
"failed to apply region @$self->{region}\n"), return |
346
|
|
|
|
|
|
|
if $track->is_region; |
347
|
|
|
|
|
|
|
$track->set(region_start => $self->{region}->[0], |
348
|
0
|
|
|
|
|
|
region_end => $self->{region}->[1]); |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
sub add { |
352
|
0
|
|
|
0
|
0
|
|
my ($self, $track, $successor) = @_; |
353
|
|
|
|
|
|
|
# TODO stop_do_start should take place at this level |
354
|
|
|
|
|
|
|
# possibly reconfiguring engine |
355
|
0
|
|
|
|
|
|
my $args = {}; |
356
|
0
|
|
|
|
|
|
$args->{before} = $successor; |
357
|
0
|
0
|
|
|
|
|
$args->{surname} = $self->name if $self->name; |
358
|
0
|
|
|
|
|
|
my $added = $self->add_ops($track, $args); |
359
|
0
|
|
|
|
|
|
$self->add_inserts($track); |
360
|
0
|
0
|
|
|
|
|
$self->add_region($track) if $self->region; |
361
|
0
|
|
|
|
|
|
$added |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
sub destroy { |
365
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
366
|
0
|
|
|
|
|
|
delete $by_index{$self->n}; |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
#### class routines |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
sub find { |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
# find(): search for an effect chain by attributes |
374
|
|
|
|
|
|
|
# |
375
|
|
|
|
|
|
|
# Returns EffectChain objects in list context, |
376
|
|
|
|
|
|
|
# number of matches in scalar context. |
377
|
|
|
|
|
|
|
|
378
|
0
|
|
|
0
|
0
|
|
my %args = @_; |
379
|
0
|
|
|
|
|
|
my $unique = delete $args{unique}; |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
# first check for a specified index that matches |
382
|
|
|
|
|
|
|
# an existing chain |
383
|
|
|
|
|
|
|
|
384
|
0
|
0
|
|
|
|
|
return $by_index{$args{n}} if $args{n}; |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
# otherwise all specified fields must match |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
my @found = grep |
389
|
0
|
|
|
|
|
|
{ my $fx_chain = $_; |
|
0
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# check if any specified fields *don't* match |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
my @non_matches = grep |
394
|
|
|
|
|
|
|
{ |
395
|
|
|
|
|
|
|
|
396
|
0
|
|
|
|
|
|
! ($fx_chain->{attrib}->{$_} eq $args{$_}) |
|
0
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
#! ($_ ne 'version' and $args{$_} eq 1 and $fx_chain->$_) |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
} keys %args; |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
# if no non-matches, then all have matched, |
403
|
|
|
|
|
|
|
# and we return true |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
! scalar @non_matches |
406
|
|
|
|
|
|
|
|
407
|
0
|
|
|
|
|
|
} values %by_index; |
408
|
|
|
|
|
|
|
|
409
|
0
|
0
|
0
|
|
|
|
warn("unique chain requested but multiple chains found. Skipping.\n"), |
410
|
|
|
|
|
|
|
return if $unique and @found > 1; |
411
|
|
|
|
|
|
|
|
412
|
0
|
0
|
|
|
|
|
if( wantarray() ){ $unique ? pop @found : sort{ $a->n cmp $b->n } @found } |
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
413
|
0
|
|
|
|
|
|
else { scalar @found } |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
sub summary { |
417
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
418
|
0
|
|
|
|
|
|
my @output; |
419
|
0
|
0
|
|
|
|
|
push @output, " name: ".$self->name if $self->name; |
420
|
0
|
0
|
|
|
|
|
push @output, " track name: ".$self->track_name if $self->track_name; |
421
|
|
|
|
|
|
|
push @output, |
422
|
|
|
|
|
|
|
map{ |
423
|
0
|
|
|
|
|
|
my $i = Audio::Nama::effect_index( $self->{ops_data}->{$_}->{type} ); |
424
|
0
|
|
|
|
|
|
my $name = " ". $fx_cache->{registry}->[$i]->{name}; |
425
|
0
|
|
|
|
|
|
} @{$_->ops_list}; |
|
0
|
|
|
|
|
|
|
426
|
0
|
|
|
|
|
|
map{ $_,"\n"} @output; |
|
0
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
sub move_attributes { |
430
|
0
|
|
|
0
|
0
|
|
my $ec_hash = shift; |
431
|
0
|
|
|
|
|
|
map { $ec_hash->{attrib}->{$_} = delete $ec_hash->{$_} } |
432
|
0
|
|
|
|
|
|
grep{ $ec_hash->{$_} } |
|
0
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
@attributes; |
434
|
|
|
|
|
|
|
} |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
0
|
|
|
sub DESTROY {} |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
{ |
440
|
|
|
|
|
|
|
#### Effect-chain and -profile routines |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
package Audio::Nama; |
443
|
|
|
|
|
|
|
sub add_effect_chain { |
444
|
0
|
|
|
0
|
|
|
my ($name, $track, $successor) = @_; |
445
|
0
|
|
|
|
|
|
my ($ec) = Audio::Nama::EffectChain::find( |
446
|
|
|
|
|
|
|
unique => 1, |
447
|
|
|
|
|
|
|
user => 1, |
448
|
|
|
|
|
|
|
name => $name, |
449
|
|
|
|
|
|
|
); |
450
|
0
|
0
|
|
|
|
|
if( $ec ){ $ec->add($Audio::Nama::this_track, $successor) } |
|
0
|
|
|
|
|
|
|
451
|
0
|
|
|
|
|
|
else { Audio::Nama::throw("$name: effect chain not found") } |
452
|
0
|
|
|
|
|
|
1; |
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
sub new_effect_profile { |
455
|
0
|
|
|
0
|
|
|
logsub("&new_effect_profile"); |
456
|
0
|
|
|
|
|
|
my ($bunch, $profile) = @_; |
457
|
0
|
|
|
|
|
|
my @tracks = bunch_tracks($bunch); |
458
|
0
|
|
|
|
|
|
Audio::Nama::pager( qq(effect profile "$profile" created for tracks: @tracks) ); |
459
|
|
|
|
|
|
|
map { |
460
|
0
|
|
|
|
|
|
Audio::Nama::EffectChain->new( |
461
|
|
|
|
|
|
|
profile => $profile, |
462
|
|
|
|
|
|
|
user => 1, |
463
|
|
|
|
|
|
|
global => 1, |
464
|
|
|
|
|
|
|
track_name => $_, |
465
|
|
|
|
|
|
|
ops_list => [ $tn{$_}->fancy_ops ], |
466
|
0
|
|
|
|
|
|
inserts_data => $tn{$_}->inserts, |
467
|
|
|
|
|
|
|
); |
468
|
|
|
|
|
|
|
} @tracks; |
469
|
|
|
|
|
|
|
} |
470
|
|
|
|
|
|
|
sub delete_effect_profile { |
471
|
0
|
|
|
0
|
|
|
logsub("&delete_effect_profile"); |
472
|
0
|
|
|
|
|
|
my $name = shift; |
473
|
0
|
|
|
|
|
|
Audio::Nama::pager( qq(deleting effect profile: $name) ); |
474
|
0
|
|
|
|
|
|
map{ $_->destroy} Audio::Nama::EffectChain::find( profile => $name ); |
|
0
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
} |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
sub apply_effect_profile { # overwriting current effects |
478
|
0
|
|
|
0
|
|
|
logsub("&apply_effect_profile"); |
479
|
0
|
|
|
|
|
|
my ($profile) = @_; |
480
|
0
|
|
|
|
|
|
my @chains = Audio::Nama::EffectChain::find(profile => $profile); |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
# add missing tracks |
483
|
0
|
|
|
|
|
|
map{ Audio::Nama::pager( "adding track $_" ); add_track($_) } |
|
0
|
|
|
|
|
|
|
484
|
0
|
|
|
|
|
|
grep{ !$tn{$_} } |
485
|
0
|
|
|
|
|
|
map{ $_->track_name } @chains; |
|
0
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
# add effect chains |
487
|
0
|
|
|
|
|
|
map{ $_->add } @chains; |
|
0
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
sub is_effect_chain { |
490
|
0
|
|
|
0
|
|
|
my $name = shift; |
491
|
0
|
|
|
|
|
|
my ($fxc) = Audio::Nama::EffectChain::find(name => $name, unique => 1); |
492
|
0
|
|
|
|
|
|
$fxc |
493
|
|
|
|
|
|
|
} |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
1; |
496
|
|
|
|
|
|
|
__END__ |