File Coverage

blib/lib/Audio/Nama/Bus.pm
Criterion Covered Total %
statement 60 198 30.3
branch 0 54 0.0
condition 0 32 0.0
subroutine 20 47 42.5
pod 0 14 0.0
total 80 345 23.1


line stmt bran cond sub pod time code
1             # ------------ Bus --------------------
2              
3             package Audio::Nama::Bus;
4 1     1   5 use Modern::Perl; use Carp;
  1     1   2  
  1         6  
  1         112  
  1         1  
  1         61  
5 1     1   5 use Audio::Nama::Log qw(logsub logpkg);
  1         1  
  1         47  
6 1     1   5 use Audio::Nama::Globals qw(:trackrw);
  1         2  
  1         219  
7             our @ISA = qw( Audio::Nama::Object );
8              
9             # share the following variables with subclasses
10              
11             our $VERSION = 1.0;
12             our (%by_name);
13 1         12 use Audio::Nama::Object qw(
14             name
15             rw
16             version
17             send_type
18             send_id
19             engine_group
20             class
21              
22 1     1   7 );
  1         2  
23             sub initialize {
24 0     0 0   %by_name = ();
25             };
26             sub new {
27 0     0 0   my $class = shift;
28 0           my %vals = @_;
29 0           my @undeclared = grep{ ! $_is_field{$_} } keys %vals;
  0            
30 0 0         croak "undeclared field: @undeclared" if @undeclared;
31 0 0         if (! $vals{name}){
32 0           Audio::Nama::throw("missing bus name");
33             return
34 0           }
35 0 0         if ( $by_name{$vals{name}} ){
36 0 0         Audio::Nama::throw("$vals{name}: bus name already exists. Skipping.")
37             unless $Audio::Nama::quiet;
38 0           return;
39             }
40 0           my $bus = bless {
41             class => $class, # for serialization, may be overridden
42             rw => MON, # for group control
43             @_ }, $class;
44 0           $by_name{$bus->name} = $bus;
45             }
46 0     0 0   sub group { $_[0]->name }
47              
48              
49             sub tracks { # returns list of track names in bus
50 0     0 0   my $bus = shift;
51 0           map{ $_->name } $bus->track_o;
  0            
52             }
53             sub track_o {
54 0     0 0   my $bus = shift;
55 0           grep{ $_->group eq $bus->name } Audio::Nama::all_tracks();
  0            
56             }
57             sub last {
58             #logpkg(__FILE__,__LINE__,'debug', "group: @_");
59 0     0 0   my $bus = shift;
60 0           my $max = 0;
61             map{
62 0           my $track = $_;
  0            
63 0           my $last;
64 0   0       $last = $track->last || 0;
65             #print "track: ", $track->name, ", last: $last\n";
66              
67 0 0         $max = $last if $last > $max;
68              
69             } $bus->track_o;
70 0           $max;
71             }
72              
73 0     0 0   sub remove { Audio::Nama::throw($_[0]->name, " is system bus. No can remove.") }
74              
75             { my %allows = (REC => 'REC/MON', MON => MON, OFF => 'OFF');
76 0     0 0   sub allows { $allows{ $_[0]->rw } }
77             }
78             { my %forces = (
79             REC => 'REC (allows REC/MON)',
80             MON => 'MON (forces REC to MON)',
81             OFF => 'OFF (enforces OFF)'
82             );
83 0     0 0   sub forces { $forces{ $_[0]->rw } }
84             }
85            
86             ## class methods
87              
88             # sub buses, and Main
89 0     0 0   sub all { grep{ ! $Audio::Nama::config->{_is_system_bus}->{$_->name} } values %by_name };
  0            
90              
91             sub overall_last {
92 0     0 0   my $max = 0;
93 0 0         map{ my $last = $_->last; $max = $last if $last > $max } all();
  0            
  0            
94 0           $max;
95             }
96             sub settings_line {
97            
98 0     0 0   my ($mix,$bus) = @_;
99            
100 0           my $nothing = '-' x 77 . "\n";
101             #return if $maybe_mix->name eq 'Master' or $maybe_mix->group eq 'Mastering';
102 0 0         return unless defined $mix;
103              
104 0           my ($bustype) = $bus->class =~ /(\w+)$/;
105 0           my $line = join " ", $bustype ,$bus->name,"is",$bus->forces;
106 0 0         $line .= " Version setting".$bus->version if $bus->version;
107             #$line .= "feeds",
108 0           $line .= " Mix track is ". $mix->rw;
109 0           $line = "------[$line]";
110 0           $line .= '-' x (77 - length $line);
111 0           $line .= "\n";
112 0           $line
113             }
114            
115             sub trackslist {
116 0     0 0   my $bus = shift;
117 0           my $mix = $Audio::Nama::tn{$bus->send_id};
118 0           my @list = ($mix,$bus);
119 0           push @list, map{$Audio::Nama::tn{$_}} ($mix->name, $bus->tracks);
  0            
120 0           \@list;
121             }
122              
123       0 0   sub apply {} # base class does no routing of its own
124              
125              
126             ### subclasses
127             {
128             package Audio::Nama::SubBus;
129 1     1   6 use Modern::Perl; use Carp; our @ISA = 'Audio::Nama::Bus';
  1     1   6  
  1         4  
  1         75  
  1         1  
  1         74  
130 1     1   6 use Audio::Nama::Log qw(logsub logpkg);
  1         1  
  1         46  
131 1     1   5 use Audio::Nama::Util qw(input_node);
  1         1  
  1         52  
132 1     1   5 use Audio::Nama::Globals qw(:trackrw);
  1         2  
  1         314  
133              
134             # connect source --> member_track --> mix_track
135              
136             sub output_is_connectable {
137 0     0     my $bus = shift;
138              
139             # Either the bus's mix track is set to REC or MON
140            
141             $bus->send_type eq 'track' and $Audio::Nama::tn{$bus->send_id}->rec_status =~ /REC|MON/
142              
143             # Or, during mixdown, we connect bus member tracks to Master
144             # even tho Master may be set to OFF
145            
146             or $bus->send_type eq 'track'
147             and $bus->send_id eq 'Master'
148 0 0 0       and $Audio::Nama::tn{Mixdown}->rec_status eq 'REC'
      0        
      0        
      0        
      0        
149            
150             # or we are connecting directly to a loop device
151             or $bus->send_type eq 'loop' and $bus->send_id =~ /^\w+_(in|out)$/;
152             }
153              
154             sub apply {
155 1     1   5 no warnings 'uninitialized';
  1         2  
  1         484  
156 0     0     my ($bus, $g) = @_;
157 0           logpkg(__FILE__,__LINE__,'debug', "bus ". $bus->name. ": applying routes");
158 0           logpkg(__FILE__,__LINE__,'debug', "Bus destination is type: $bus->{send_type}, id: $bus->{send_id}");
159             map{
160             # connect member track input paths
161 0           logpkg(__FILE__,__LINE__,'debug', "track ".$_->name);
162 0           my @path = $_->input_path;
163 0 0         $g->add_path(@path) if @path;
164 0 0         logpkg(__FILE__,__LINE__,'debug',"input path: @path") if scalar @path;
165              
166 0           logpkg(__FILE__,__LINE__,'debug', join " ", "bus output:", $_->name, $bus->send_id);
167              
168             # connect member track outputs to target
169             # disregard Master track rec_status when connecting
170             # Main bus during mixdown handling
171              
172 0 0         Audio::Nama::Graph::add_path_for_send($g, $_->name, $bus->send_type, $bus->send_id )
173             if $bus->output_is_connectable;
174            
175             # add paths for recording
176            
177 0 0 0       Audio::Nama::Graph::add_path_for_rec($g,$_)
      0        
178             if $_->rec_status eq REC
179             and ! $Audio::Nama::mode->preview and ! $Audio::Nama::mode->doodle;
180              
181 0           } grep {$_->rec_status ne OFF} $bus->track_o;
  0            
182             }
183             sub remove {
184 0     0     my $bus = shift;
185              
186             # all tracks returned to Main group
187 0           map{$_->set(group => 'Main') } $bus->track_o;
  0            
188              
189 0           my $mix_track = $Audio::Nama::tn{$bus->name};
190              
191 0 0         if ( defined $mix_track ){
192            
193 0           $mix_track->unbusify;
194            
195             # remove mix track unless it has some WAV files
196              
197 0 0         $mix_track->remove unless scalar @{ $mix_track->versions };
  0            
198             }
199              
200             # remove bus from index
201            
202 0           delete $Audio::Nama::bn{$bus->name};
203             }
204             }
205             {
206             package Audio::Nama::SendBusRaw;
207 1     1   5 use Modern::Perl; use Carp; our @ISA = 'Audio::Nama::Bus';
  1     1   2  
  1         4  
  1         65  
  1         2  
  1         71  
208 1     1   5 use Audio::Nama::Log qw(logsub logpkg);
  1         2  
  1         304  
209             sub apply {
210 0     0     my $bus = shift;
211             map{
212 0           my @input_path = $_->input_path;
213 0           $Audio::Nama::g->add_edge(@input_path);
214             $Audio::Nama::g->set_edge_attributes( @input_path,
215 0           { width => $Audio::Nama::tn{$_->target}->width });
216 0           my @edge = ($_->name, Audio::Nama::output_node($bus->send_type));
217 0           $Audio::Nama::g->add_edge(@edge);
218 0           $Audio::Nama::g->set_edge_attributes( @edge, {
219             send_id => $bus->send_id,
220             width => 2 }); # force to stereo
221 0           } grep{ $_->input_path } $bus->track_o;
  0            
222             }
223             sub remove {
224 0     0     my $bus = shift;
225              
226             # delete all tracks
227 0           map{$_->remove } $bus->track_o;
  0            
228              
229             # remove bus
230 0           delete $by_name{$bus->name};
231             }
232             }
233             {
234             package Audio::Nama::SendBusCooked;
235 1     1   5 use Audio::Nama::Log qw(logsub logpkg);
  1         3  
  1         41  
236 1     1   5 use Modern::Perl; use Carp; our @ISA = 'Audio::Nama::SendBusRaw';
  1     1   1  
  1         9  
  1         61  
  1         2  
  1         219  
237              
238             # graphic routing: target -> slave -> bus_send_type
239              
240             sub apply {
241 0     0     my $bus = shift;
242 0           my $g = shift;
243 0           map{ my @edge = ($_->name, Audio::Nama::output_node($bus->send_type));
  0            
244 0           $g->add_path( $_->target, @edge);
245 0           $g->set_edge_attributes( @edge, {
246             send_type => $bus->send_type,
247             send_id => $bus->send_id,
248             width => 2})
249             } $bus->track_o;
250             }
251              
252             }
253              
254             # ---------- Bus routines --------
255             {
256             package Audio::Nama;
257 1     1   6 use Modern::Perl; use Carp;
  1     1   2  
  1         4  
  1         62  
  1         2  
  1         52  
258 1     1   5 use Audio::Nama::Util qw(dest_type);
  1         2  
  1         1098  
259             our (
260             $this_track,
261             $this_bus,
262             %tn,
263             %bn,
264             );
265              
266             sub set_current_bus {
267 0   0 0     my $track = shift || ($this_track ||= $tn{Master});
268              
269 0 0         return unless $track; # needed for test environment
270              
271             # The current sequence changes when the user touches a
272             # track that belongs to another sequence.
273            
274 0 0         $this_sequence = $bn{$track->group} if (ref $bn{$track->group}) =~ /Sequence/;
275              
276 0 0         my $bus_name =
    0          
277             $track->name =~ /Master|Mixdown/
278             ? 'Main'
279             : $track->is_mix_track()
280             ? $track->name
281             : $track->group;
282            
283 0           select_bus($bus_name);
284             }
285             sub select_bus {
286 0     0     my $name = shift;
287 0 0         my $bus = $bn{$name} or return;
288 0           $this_bus = $name;
289 0           $this_bus_o = $bus;
290             }
291             sub add_bus {
292 0     0     my ($name, @args) = @_;
293            
294             Audio::Nama::SubBus->new(
295             name => $name,
296             send_type => 'track',
297             send_id => $name,
298 0 0         ) unless $Audio::Nama::Bus::by_name{$name};
299              
300 0           @args = (
301             rw => MON,
302             @args
303             );
304              
305 0 0         $tn{$name} and Audio::Nama::pager_newline( qq($name: setting as mix track for bus "$name"));
306              
307 0   0       my $track = $tn{$name}// add_track($name, width => 2);
308              
309 0           $track->set( @args );
310            
311             }
312            
313             sub add_submix {
314              
315 0     0     my ($name, $dest_id, $bus_type) = @_;
316 0           my $dest_type = dest_type( $dest_id );
317              
318             # dest_type: soundcard | jack_client | loop | jack_port | jack_multi
319            
320 0           logpkg(__FILE__,__LINE__,'debug',"name: $name, dest_type: $dest_type, dest_id: $dest_id");
321 0 0 0       if ($bn{$name} and (ref $bn{$name}) !~ /SendBus/){
322 0           Audio::Nama::throw($name,": bus name already in use. Aborting."), return;
323             }
324 0 0         if ($bn{$name}){
325 0           Audio::Nama::pager_newline( qq(monitor bus "$name" already exists. Updating with new tracks.) );
326             } else {
327 0           my @args = (
328             name => $name,
329             send_type => $dest_type,
330             send_id => $dest_id,
331             );
332              
333 0 0         my $class = $bus_type eq 'cooked' ? 'Audio::Nama::SendBusCooked' : 'Audio::Nama::SendBusRaw';
334 0           my $bus = $class->new( @args );
335              
336 0 0         $bus or carp("can't create bus!\n"), return;
337              
338             }
339 0           map{ Audio::Nama::EarTrack->new( name => "$name\_$_", # BusName_TrackName
340             rw => MON,
341             target => $_,
342             group => $name,
343             width => 2,
344             hide => 1,
345             )
346 0           } $bn{Main}->tracks;
347            
348             }
349              
350            
351             sub update_submix {
352 0     0     my $name = shift;
353             add_submix( $name,
354 0           $bn{$name}->send_id),
355             "dummy",
356             }
357             sub remove_submix_helper_tracks {
358 0     0     my $name = shift;
359             #say "got name: $name";
360 0           my @submixes = submixes();
361             #say "got submixes:", Dumper \@submixes;
362 0           for my $sm ( @submixes ){
363 0           my $to_remove = join '_', $sm->name, $name;
364             #say "to_remove: $to_remove";
365 0           local $quiet;
366 0           $quiet++;
367 0           for my $name ($sm->tracks) {
368 0 0         $tn{$name}->remove, last if $name eq $to_remove
369             }
370             }
371              
372             }
373 0     0     sub submixes { grep { (ref $_) =~ /SendBusCooked/ } values %Audio::Nama::Bus::by_name }
  0            
374              
375             }
376             1;
377             __END__