File Coverage

blib/lib/Audio/Nama/Insert.pm
Criterion Covered Total %
statement 58 193 30.0
branch 0 26 0.0
condition 0 15 0.0
subroutine 20 42 47.6
pod 0 15 0.0
total 78 291 26.8


line stmt bran cond sub pod time code
1             {
2             package Audio::Nama::Insert;
3 1     1   5 use Modern::Perl;
  1         2  
  1         7  
4 1     1   109 use Carp;
  1         2  
  1         55  
5 1     1   5 no warnings qw(uninitialized redefine);
  1         2  
  1         54  
6             our $VERSION = 0.1;
7             our %by_index;
8 1     1   4 use Audio::Nama::Log qw(logpkg);
  1         2  
  1         53  
9 1     1   4 use Audio::Nama::Log qw(logpkg);
  1         2  
  1         41  
10 1     1   6 use Audio::Nama::Globals qw($jack $setup $config :trackrw);
  1         2  
  1         156  
11 1         7 use Audio::Nama::Object qw(
12             n
13             class
14             send_type
15             send_id
16             return_type
17             return_id
18             wet_track
19             dry_track
20             tracks
21             track
22             wetness
23             wet_vol
24             dry_vol
25              
26 1     1   6 );
  1         2  
27              
28 1     1   5 use Audio::Nama::Util qw(input_node output_node dest_type);
  1         2  
  1         1600  
29              
30             initialize();
31              
32 1     1 0 3 sub initialize { %by_index = () }
33              
34             sub idx { # return first free index
35 0     0 0   my $n = 0;
36 0           while (++$n){
37 0 0         return $n if not $by_index{$n}
38             }
39             }
40              
41             sub wet_name {
42 0     0 0   my $self = shift;
43 0           join('-', $self->track, $self->n, 'wet');
44             }
45             sub dry_name {
46 0     0 0   my $self = shift;
47 0           join('-', $self->track, $self->n, 'dry');
48             }
49             sub new {
50 0     0 0   my $class = shift;
51 0           my %vals = @_;
52 0           my @undeclared = grep{ ! $_is_field{$_} } keys %vals;
  0            
53 0 0         croak "undeclared field: @undeclared" if @undeclared;
54 0   0       $vals{n} ||= idx();
55 0           my $self = bless {
56             class => $class, # for restore
57             wetness => 100,
58             %vals,
59             }, $class;
60 0           my $name = $vals{track};
61              
62             # this is the wet return track
63            
64 0           my $wet = Audio::Nama::SlaveTrack->new(
65             name => $self->wet_name,
66             target => $name,
67             group => 'Insert',
68             rw => MON,
69            
70             # don't hide wet track if used for hosting effects
71            
72             hide => ! $self->is_local_effects_host,
73             );
74 0           my $dry = Audio::Nama::SlaveTrack->new(
75             name => $self->dry_name,
76             target => $name,
77             group => 'Insert',
78             hide => 1,
79             rw => MON);
80 0           map{ Audio::Nama::remove_effect($_)} $wet->vol, $wet->pan, $dry->vol, $dry->pan;
  0            
81 0           map{ my $track = $_; map{ delete $track->{$_} } qw(vol pan) } $wet, $dry;
  0            
  0            
  0            
82              
83 0           $self->{dry_vol} = Audio::Nama::add_effect({
84             track => $dry,
85             type => 'ea',
86             params => [0]
87             });
88 0           $self->{wet_vol} = Audio::Nama::add_effect({
89             track => $wet,
90             type => 'ea',
91             params => [100],
92             });
93             # synchronize effects with wetness setting
94 0           $self->set_wetness($self->{wetness});
95 0           $by_index{$self->n} = $self;
96             }
97              
98             # method name for track field holding insert
99              
100 0 0   0 0   sub type { (ref $_[0]) =~ /Pre/ ? 'prefader_insert' : 'postfader_insert' }
101              
102             #sub remove {}
103             # subroutine
104             #
105             sub add_insert {
106 0     0 0   my ($track, $type, $send_id, $return_id) = @_;
107 0           local $Audio::Nama::this_track;
108             # $type : prefader_insert | postfader_insert
109 0           Audio::Nama::pager("\n",$track->name , ": adding $type\n");
110 0           my $name = $track->name;
111              
112             # the input fields will be ignored, since the track will get input
113             # via the loop device track_insert
114            
115 0 0         my $class = $type =~ /pre/ ? 'Audio::Nama::PreFaderInsert' : 'Audio::Nama::PostFaderInsert';
116            
117             # remove an existing insert of specified type, if present
118 0 0         $track->$type and $by_index{$track->$type}->remove;
119              
120 0           my $i = $class->new(
121             track => $track->name,
122             send_type => Audio::Nama::dest_type($send_id),
123             send_id => $send_id,
124             return_type => Audio::Nama::dest_type($return_id),
125             return_id => $return_id,
126             );
127 0 0         if (! $i->{return_id}){
128 0           $i->{return_type} = $i->{send_type};
129 0 0         $i->{return_id} = $i->{send_id} if $i->{return_type} eq 'jack_client';
130 0 0         $i->{return_id} = $i->{send_id} + 2 if $i->{return_type} eq 'soundcard';
131             # TODO adjust to suit track channel width?
132             }
133             }
134             sub get_id {
135             # get Insert index for track
136            
137             # optionally specify whether we are looking for
138             # prefader or postfader insert
139            
140             #
141 0     0 0   my ($track, $prepost) = @_;
142 0           my @inserts = get_inserts($track->name);
143 0           my ($prefader) = (map{$_->n}
144 0           grep{$_->class =~ /pre/i}
  0            
145             @inserts);
146 0           my ($postfader) = (map{$_->n}
147 0           grep{$_->class =~ /post/i}
  0            
148             @inserts);
149 0           my %id = ( pre => $prefader, post => $postfader);
150             $prepost = $id{pre} ? 'pre' : 'post'
151 0 0 0       if (! $prepost and ! $id{pre} != ! $id{post} );
    0          
152 0           $id{$prepost};;
153             }
154             sub get_inserts {
155 0     0 0   my $trackname = shift;
156 0           grep{ $_-> track eq $trackname } values %by_index;
  0            
157             }
158              
159              
160 0     0 0   sub is_local_effects_host { ! $_[0]->send_id }
161              
162             sub set_wetness {
163 0     0 0   my ($self, $p) = @_;
164 0           $self->{wetness} = $p;
165 0           Audio::Nama::modify_effect($self->wet_vol, 1, undef, $p);
166 0           Audio::Nama::sleeper(0.1);
167 0           Audio::Nama::modify_effect($self->dry_vol, 1, undef, 100 - $p);
168             }
169 0           sub is_via_soundcard {
170 0     0 0   my $self = shift;
171            
172 0           for (qw(source send)){
173 0           my $type = "$_\_type";
174 0           my $id = "$_\_id";
175 0 0 0       return 0 unless is_channel($self->$id)
      0        
176             or $self->$type eq 'soundcard'
177             or is_jack_soundcard($self->$id)
178             }
179 0     0 0   sub is_channel { $_[0] =~ /^\d+$/ }
180 0     0 0   sub is_jack_soundcard { $_[0] =~ /^system/ }
181             }
182             sub soundcard_delay {
183 0     0 0   my $track_name = shift;
184 0           my ($insert) = grep{ $_->wet_name eq $track_name } values %by_index;
  0            
185 0           my $delta = 0;
186             $delta = $config->{soundcard_loopback_delay}
187 0 0 0       if defined $insert and $insert->is_via_soundcard;
188 0           Audio::Nama::Lat->new($delta,$delta)
189             }
190             }
191             {
192             package Audio::Nama::PostFaderInsert;
193 1     1   5 use Modern::Perl; use Carp; our @ISA = qw(Audio::Nama::Insert);
  1     1   2  
  1         5  
  1         103  
  1         2  
  1         68  
194 1     1   6 use Audio::Nama::Util qw(input_node output_node dest_type);
  1         1  
  1         57  
195 1     1   5 use Audio::Nama::Log qw(logpkg);
  1         1  
  1         70  
196             sub add_paths {
197              
198             # Since this routine will be called after expand_graph,
199             # we can be sure that every track vertex will connect to
200             # to a single edge, either loop or an output
201            
202 0     0     my ($self, $g, $name) = @_;
203 1     1   5 no warnings qw(uninitialized);
  1         2  
  1         588  
204 0           Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "add_insert for track: $name");
205              
206 0           my $t = $Audio::Nama::tn{$name};
207              
208              
209 0     0     Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "insert structure: ", sub{$self->dump});
  0            
210              
211 0           my ($successor) = $g->successors($name);
212              
213             # successor will be either a loop, device or JACK port
214             # i.e. can accept multiple signals
215              
216 0           $g->delete_edge($name, $successor);
217 0           my $loop = "$name\_insert_post";
218 0           my $wet = $Audio::Nama::tn{$self->wet_name};
219 0           my $dry = $Audio::Nama::tn{$self->dry_name};
220              
221 0           Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "found wet: ", $wet->name, " dry: ",$dry->name);
222              
223             # if no insert target, our insert will
224             # a parallel effects host with wet/dry dry branches
225            
226             # --- track ---insert_post--+--- wet ---+-- successor
227             # | |
228             # +--- dry ---+
229              
230             # otherwise a conventional wet path with send and receive arms
231            
232             # --- track ---insert_post--+-- wet-send wet-return ---+-- successor
233             # | |
234             # +-------------- dry ----------+
235            
236 0 0         if ( $self->is_local_effects_host )
237             {
238 0           $g->add_path($name, $loop, $wet->name, $successor);
239              
240             }
241             else
242              
243             {
244             # wet send path (no extra track): track -> loop -> output
245              
246 0           my @edge = ($loop, output_node($self->{send_type}));
247 0           Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "edge: @edge");
248 0           $g->add_path( $name, @edge);
249 0           $g->set_vertex_attributes($loop, {n => $t->n});
250             $g->set_edge_attributes(@edge, {
251             send_id => $self->{send_id},
252 0           width => 2,
253             });
254             # wet return path: input -> wet_track (slave) -> successor
255            
256             # we override the input with the insert's return source
257              
258             $g->set_vertex_attributes($wet->name, {
259             width => 2, # default for cooked
260             mono_to_stereo => '', # override
261             source_type => $self->{return_type},
262             source_id => $self->{return_id},
263 0           });
264 0           $g->add_path(input_node($self->{return_type}), $wet->name, $successor);
265              
266             }
267              
268             # connect dry track to graph
269            
270 0           $g->add_path($loop, $dry->name, $successor);
271             }
272            
273             sub remove {
274 0     0     my $self = shift;
275 0           $Audio::Nama::tn{ $self->wet_name }->remove;
276 0           $Audio::Nama::tn{ $self->dry_name }->remove;
277 0           delete $Audio::Nama::Insert::by_index{$self->n};
278             }
279             }
280             {
281             package Audio::Nama::PreFaderInsert;
282 1     1   6 use Modern::Perl; use Carp; our @ISA = qw(Audio::Nama::Insert);
  1     1   2  
  1         4  
  1         114  
  1         2  
  1         73  
283 1     1   5 use Audio::Nama::Util qw(input_node output_node dest_type);
  1         3  
  1         56  
284 1     1   5 use Audio::Nama::Log qw(logpkg);
  1         2  
  1         42  
285 1     1   5 use Audio::Nama::Globals qw(:trackrw);
  1         2  
  1         353  
286              
287              
288             # --- source ---------- wet_send_track wet_return_track -+-- insert_pre -- track
289             # |
290             # --- source ------------------ dry track ----------------+
291              
292             sub new {
293 0     0     my ($class, %args) = @_;
294 0           my $self = $class->SUPER::new(%args);
295              
296 0           my $wet_send = Audio::Nama::SlaveTrack->new(
297             name => $self->wet_send_name,
298             target => $self->track,
299             group => 'Insert',
300             hide => 1,
301             rw => REC
302             );
303 0           map{ Audio::Nama::remove_effect($_)} $wet_send->vol, $wet_send->pan;
  0            
304 0           map{ my $track = $_; map{ delete $track->{$_} } qw(vol pan) } $wet_send;
  0            
  0            
  0            
305 0           $self
306             }
307             sub wet_send_name {
308 0     0     my $self = shift;
309 0           join('-', $self->track, $self->n, 'wet-send');
310             }
311            
312            
313              
314             sub add_paths {
315 0     0     my ($self, $g, $name) = @_;
316 1     1   6 no warnings qw(uninitialized);
  1         2  
  1         489  
317 0           Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "add_insert for track: $name");
318              
319 0           my $t = $Audio::Nama::tn{$name};
320              
321              
322 0     0     Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "insert structure:", sub{$self->dump});
  0            
323              
324             # get track source
325            
326 0           my ($predecessor) = $g->predecessors($name);
327              
328             # delete source connection to track
329            
330 0           $g->delete_edge($predecessor, $name);
331 0           my $loop = "$name\_insert_pre";
332              
333 0           my $wet = $Audio::Nama::tn{$self->wet_name};
334 0           my $dry = $Audio::Nama::tn{$self->dry_name};
335 0           my $wet_send = $Audio::Nama::tn{$self->wet_send_name};
336              
337 0           Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "found wet: ", $wet->name, " dry: ",$dry->name);
338              
339             #pre: wet send path: wet_send_name (slave) -> output
340              
341 0           my @edge = ($self->wet_send_name, output_node($self->send_type));
342 0           $g->add_path($predecessor, @edge);
343 0           Audio::Nama::logpkg(__FILE__,__LINE__,'debug', "edge: @edge");
344             $g->set_vertex_attributes($self->wet_send_name, {
345             send_id => $self->{send_id},
346             send_type => $self->{send_type},
347 0           mono_to_stereo => '', # disable for prefader send path
348             });
349              
350             #pre: wet return path: input -> wet_track (slave) -> loop
351              
352            
353             # we override the input with the insert's return source
354              
355             $g->set_vertex_attributes($wet->name, {
356             width => $t->width,
357             mono_to_stereo => '', # override
358             source_type => $self->{return_type},
359             source_id => $self->{return_id},
360 0           });
361 0           $g->set_vertex_attributes($dry->name, {
362             mono_to_stereo => '', # override
363             });
364 0           $g->add_path(input_node($self->{return_type}), $wet->name, $loop);
365              
366             # connect dry track to graph
367             #
368             # post: dry path: loop -> dry -> successor
369             # pre: dry path: predecessor -> dry -> loop
370            
371 0           $g->add_path($predecessor, $dry->name, $loop, $name);
372             }
373            
374             sub remove {
375 0     0     my $self = shift;
376 0           $Audio::Nama::tn{ $self->wet_send_name }->remove;
377 0           $Audio::Nama::tn{ $self->dry_name }->remove;
378 0           $Audio::Nama::tn{ $self->wet_name }->remove;
379 0           delete $Audio::Nama::Insert::by_index{$self->n};
380             }
381             }
382             1;