| 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
|
|
5
|
use Modern::Perl; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
6
|
|
|
11
|
1
|
|
|
1
|
|
114
|
use Data::Dumper::Concise; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
56
|
|
|
12
|
1
|
|
|
1
|
|
5
|
use Carp; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
57
|
|
|
13
|
1
|
|
|
1
|
|
5
|
use Exporter qw(import); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
33
|
|
|
14
|
1
|
|
|
1
|
|
5
|
use Storable qw(dclone); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
52
|
|
|
15
|
1
|
|
|
1
|
|
5
|
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
|
|
|
|
|
48
|
|
|
17
|
1
|
|
|
1
|
|
5
|
use Audio::Nama::Assign qw(json_out); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
43
|
|
|
18
|
|
|
|
|
|
|
|
|
19
|
1
|
|
|
1
|
|
5
|
use Audio::Nama::Globals qw($fx_cache %tn $fx); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
121
|
|
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
our $AUTOLOAD; |
|
22
|
|
|
|
|
|
|
our $VERSION = 0.001; |
|
23
|
1
|
|
|
1
|
|
5
|
no warnings qw(uninitialized); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
85
|
|
|
24
|
|
|
|
|
|
|
our @ISA; |
|
25
|
|
|
|
|
|
|
our ($n, %by_index, @attributes, %is_attribute); |
|
26
|
1
|
|
|
|
|
12
|
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
|
|
|
|
|
2
|
|
|
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__ |