File Coverage

blib/lib/Audio/Nama/Edit.pm
Criterion Covered Total %
statement 27 360 7.5
branch 0 124 0.0
condition 0 39 0.0
subroutine 9 79 11.3
pod 0 26 0.0
total 36 628 5.7


line stmt bran cond sub pod time code
1             {
2             package Audio::Nama::Edit;
3 1     1   5 use Audio::Nama::Globals qw(:singletons :trackrw);
  1         2  
  1         228  
4              
5             # each edit is identified by:
6             # - host track name
7             # - host track version
8             # - edit name (i.e. sax-v1) used as key in %by_name
9             #
10              
11 1     1   6 use Modern::Perl;
  1         2  
  1         6  
12             our $VERSION = 1.0;
13 1     1   124 use Carp;
  1         1  
  1         59  
14 1     1   6 no warnings qw(uninitialized);
  1         2  
  1         41  
15             our @ISA;
16 1     1   5 use vars qw($n %by_index %by_name );
  1         2  
  1         59  
17 1         7 use Audio::Nama::Object qw(
18             n
19             play_start_mark_name
20             rec_start_mark_name
21             rec_end_mark_name
22             host_track
23             host_version
24             fades
25 1     1   6 );
  1         1  
26              
27             sub initialize {
28 0     0 0   $n = 0;
29 0           %by_name = ();
30 0           %by_index = ();
31 0           @Audio::Nama::edits_data = (); # for save/restore
32             }
33              
34 0     0 0   sub next_n { ++$n }
35              
36             sub new {
37 0     0 0   my $class = shift;
38 0           my %vals = @_;
39              
40 0 0         croak "undeclared field: @_" if grep{ ! $_is_field{$_} } keys %vals;
  0            
41              
42 0           my $self = bless
43             {
44             n => next_n(),
45             fades => [],
46             @_
47             }, $class;
48              
49 0           $by_name{ $self->edit_name } = $self;
50 0           $by_index{ $self->n } = $self;
51              
52             #print "self class: $class, self type: ", ref $self, $/;
53              
54 0           my $name = $self->host_track;
55 0           my $host = $Audio::Nama::tn{$name};
56 0 0         confess( Audio::Nama::project_dir().": missing host_track". $Audio::Nama::this_track->dump. $self->dump. Audio::Nama::process_command("dumpa")) if !$host;
57              
58             # Routing:
59             #
60             # sax-v5-original --+
61             # |
62             # sax-v5-edit1 -----+--- sax-v5 (bus/track) --- sax (bus/track) -----
63              
64              
65             # prepare top-level bus and mix track
66            
67 0           $host->busify; # i.e. sax (bus/track)
68            
69             # create the version-level bus and mix track
70             # i.e. sax-v5 (bus/track)
71              
72             # (maybe it already exists)
73            
74 0           my $version_mix = Audio::Nama::Track->new(
75              
76             name => $self->edit_root_name, # i.e. sax-v5
77             # rw => REC, # set by ->busify
78             source_type => 'bus',
79             source_id => 'bus',
80             width => 2, # default to stereo
81             group => $self->host_track, # i.e. sax
82             hide => 1,
83             );
84 0           $version_mix->busify;
85              
86             # create host track alias if necessary
87              
88             # To ensure that users don't get into trouble, we would like to
89             # restrict this track:
90             # - version number must *not* be allowed to change
91             # - rw setting must be fixed to PLAY #
92             # The easiest way may be to subclass the 'set' routine
93            
94 0   0       my $host_track_alias = $Audio::Nama::tn{$self->host_alias} //
95             Audio::Nama::VersionTrack->new(
96             name => $self->host_alias,
97             version => $host->monitor_version, # static
98             target => $host->name,
99             rw => PLAY, # do not REC
100             group => $self->edit_root_name, # i.e. sax-v5
101             hide => 1,
102             );
103              
104             # create edit track
105             # - same name as edit
106             # - we expect to record
107             # - source_type and source_id come from host track
108            
109 0           my $edit_track = Audio::Nama::EditTrack->new(
110             name => $self->edit_name,
111             rw => REC,
112             source_type => $host->source_type,
113             source_id => $host->source_id,
114             group => $self->edit_root_name, # i.e. sax-v5
115             hide => 1,
116             );
117 0           $self
118             }
119             sub edit_root_name {
120 0     0 0   my $self = shift;
121 0           join '-', $self->host_track, 'v'.$self->host_version;
122             }
123             sub edit_name {
124 0     0 0   my $self = shift;
125 0           join '-', $self->edit_root_name, 'edit'.$self->n
126             }
127             sub host_alias {
128 0     0 0   my $self = shift;
129 0           join '-', $self->edit_root_name, 'original'
130             }
131              
132             # default mark names
133              
134             sub play_start_name {
135 0     0 0   my $self = shift;
136 0 0         $self->play_start_mark_name || (join '-', $self->edit_name,'play-start')
137             }
138             sub rec_start_name {
139 0     0 0   my $self = shift;
140 0 0         $self->rec_start_mark_name || (join '-', $self->edit_name,'rec-start')
141             }
142             sub rec_end_name {
143 0     0 0   my $self = shift;
144 0 0         $self->rec_end_mark_name || (join '-', $self->edit_name,'rec-end')
145             }
146              
147 0     0 0   sub play_start_mark { $Audio::Nama::Mark::by_name{$_[0]->play_start_name} }
148 0     0 0   sub rec_start_mark { $Audio::Nama::Mark::by_name{$_[0]->rec_start_name} }
149 0     0 0   sub rec_end_mark { $Audio::Nama::Mark::by_name{$_[0]->rec_end_name} }
150              
151             # the following are unadjusted values
152              
153             sub play_start_time {
154 0     0 0   my $self = shift;
155 0           $self->marktime('play_start_name')
156             }
157             sub rec_start_time {
158 0     0 0   my $self = shift;
159 0           $self->marktime('rec_start_name')
160             }
161             sub rec_end_time {
162 0     0 0   my $self = shift;
163 0           $self->marktime('rec_end_name')
164             }
165             sub play_end_time {
166 0     0 0   my $self = shift;
167             $self->marktime('rec_end_name') + $config->{edit_playback_end_margin}
168 0           }
169              
170             sub marktime {
171 0     0 0   my ($self,$markfield) = @_;
172             $Audio::Nama::Mark::by_name{$self->$markfield}->{time}
173 0           }
174              
175             sub store_fades { # replacing previous
176 0     0 0   my $edit = shift;
177 0           my @fades = @_;
178 0           my @indices = map{$_->n} @fades;
  0            
179 0           $edit->remove_fades;
180 0           $edit->set(fades => \@indices)
181             }
182             sub remove_fades {
183 0     0 0   my $edit = shift;
184 0           map{ $_->remove } map{ $Audio::Nama::Fade::by_index{$_} } @{$edit->fades};
  0            
  0            
  0            
185 0           $edit->set(fades => []);
186             }
187              
188             sub destroy {
189 0     0 0   my $edit = shift;
190              
191             # remove object from index hash
192 0           delete $by_index{$edit->n};
193 0           delete $by_name{$edit->edit_name};
194              
195             # list edit track WAV files
196 0           my @wavs = values %{$edit->edit_track->targets};
  0            
197              
198             # track removal also takes care of fades # VERIFY
199             # my $fades = $edit->fades;
200             # map{ $Audio::Nama::Fade::by_index{$_}->remove } @$fades;
201              
202             # remove edit track
203 0           $edit->edit_track->remove;
204              
205             my @sister_edits = grep{
206 0 0         $edit->host_track eq $_->host_track
  0            
207             and $edit->host_version == $_->host_version
208             } values %by_index;
209              
210             # if we are the last edit, remove all auxiliary tracks/buses
211            
212 0 0         if ( ! @sister_edits ){
213              
214 0           $edit->host_alias_track->remove;
215              
216 0           $edit->version_bus->remove;
217             # note: bus->remove will not delete a mix track with WAV files
218              
219             # The host may have a version symlinked to a WAV file
220             # belonging to the version mix track. So we remove
221             # the track, but not the wav files.
222              
223 0 0         $edit->version_mix->remove if defined $edit->version_mix;
224              
225 0           $edit->host_bus->remove;
226             }
227             # remove edit track WAV files if we've reached here
228             map{
229 0           my $path = Audio::Nama::join_path(Audio::Nama::this_wav_dir(), $_);
  0            
230 0           Audio::Nama::pager("removing $path");
231             #unlink $path;
232             } @wavs;
233             }
234              
235 0     0 0   sub host { $Audio::Nama::tn{$_[0]->host_track} } # top-level mix track, i.e. 'sax'
236 0     0 0   sub host_bus { $Audio::Nama::Bus::by_name{$_[0]->host_track} } # top-level bus
237 0     0 0   sub version_mix { $Audio::Nama::tn{$_[0]->edit_root_name} } # in top-level bus
238 0     0 0   sub version_bus { $Audio::Nama::Bus::by_name{$_[0]->edit_root_name} } # version-level bus
239 0     0 0   sub host_alias_track{ $Audio::Nama::tn{$_[0]->host_alias} } # in version_bus
240 0     0 0   sub edit_track { $Audio::Nama::tn{$_[0]->edit_name} } # in version_bus
241              
242             # utility routines
243              
244             }
245             # -------- Edit routines; Main Namespace ------
246             {
247             package Audio::Nama;
248 1     1   6 use Modern::Perl; use Carp;
  1     1   2  
  1         4  
  1         106  
  1         2  
  1         60  
249 1     1   5 no warnings 'uninitialized';
  1         2  
  1         5557  
250              
251             our (
252             %tn,
253             %ti,
254             %bn,
255             $this_track,
256             $this_edit,
257             );
258            
259              
260             sub detect_keystroke_p {
261             $project->{events}->{stdin} = AE::io(*STDIN, 0, sub {
262 0     0     &{$text->{term_attribs}->{'callback_read_char'}}();
  0            
263            
264             abort_set_edit_points(), return
265             if $text->{term_attribs}->{line_buffer} eq "q"
266 0 0 0       or $text->{term_attribs}->{line_buffer} eq "Q";
267              
268 0 0 0       if ( $text->{term_attribs}->{line_buffer} eq "p"
269 0           or $text->{term_attribs}->{line_buffer} eq "P"){ get_edit_mark()}
270 0           else{ reset_input_line() }
271 0     0     });
272             }
273              
274             sub reset_input_line {
275 0     0     $text->{term_attribs}->{line_buffer} = q();
276 0           $text->{term_attribs}->{point} = 0;
277 0           $text->{term_attribs}->{end} = 0;
278             }
279              
280              
281             { my $p;
282             my @_edit_points;
283             my @names = qw(dummy play-start rec-start rec-end);
284              
285             sub initialize_edit_points {
286 0     0     $p = 0;
287 0           @_edit_points = ();
288             }
289             sub abort_set_edit_points {
290 0     0     Audio::Nama::throw("...Aborting!");
291 0           reset_input_line();
292 0           eval_iam('stop');
293 0           initialize_edit_points();
294 0           detect_spacebar();
295             }
296              
297             sub get_edit_mark {
298 0     0     $p++;
299 0 0         if($p <= 3){ # record mark
300 0           my $pos = eval_iam('getpos');
301 0           push @_edit_points, $pos;
302 0           Audio::Nama::pager(" got $names[$p] position ".d1($pos));
303 0           reset_input_line();
304 0 0         if( $p == 3){ complete_edit_points() }
  0            
305             else{
306 0           $text->{term}->stuff_char(10);
307 0           &{$text->{term_attribs}->{'callback_read_char'}}();
  0            
308             }
309             }
310             }
311             sub complete_edit_points {
312 0     0     @{$setup->{edit_points}} = @_edit_points; # save to global
  0            
313 0           eval_iam('stop');
314 0           Audio::Nama::pager("\nEngine is stopped\n");
315 0           detect_spacebar();
316 0           print prompt(), " ";
317             }
318             }
319             sub set_edit_points {
320 0 0   0     $tn{$this_edit->edit_name}->set(rw => OFF) if defined $this_edit;
321 0 0         Audio::Nama::throw("You must use a playback-only mode to setup edit marks. Aborting"),
322             return 1 if Audio::Nama::ChainSetup::really_recording();
323 0 0         Audio::Nama::throw("You need stop the engine first. Aborting"),
324             return 1 if engine_running();
325 0           Audio::Nama::pager("Ready to set edit points!");
326 0           sleeper(0.2);
327 0           Audio::Nama::pager(q(Press the "P" key three times to mark positions for:
328             + play-start
329             + record-start
330             + record-end
331              
332             Press "Q" to quit.
333              
334             Engine will start in 2 seconds.));
335 0           initialize_edit_points();
336             $project->{events}->{set_edit_points} = AE::timer(2, 0,
337             sub {
338 0     0     reset_input_line();
339 0           detect_keystroke_p();
340 0           eval_iam('start');
341 0           Audio::Nama::pager("\n\nEngine is running\n");
342 0           print prompt();
343 0           });
344             }
345             sub transfer_edit_points {
346             Audio::Nama::throw("Use 'set_edit_points' command to specify edit region"), return
347 0 0   0     unless scalar @{$setup->{edit_points}};
  0            
348 0           my $edit = shift;
349 0           Audio::Nama::Mark->new( name => $edit->play_start_name, time => $setup->{edit_points}->[0]);
350 0           Audio::Nama::Mark->new( name => $edit->rec_start_name, time => $setup->{edit_points}->[1]);
351 0           Audio::Nama::Mark->new( name => $edit->rec_end_name, time => $setup->{edit_points}->[2]);
352 0           @{$setup->{edit_points}} = ();
  0            
353             }
354              
355       0     sub generate_edit_record_setup { # for current edit
356             # set edit track to REC
357             # set global region start offset
358             # set global region length cutoff
359             # set regenerate_setup flag
360             # insert host track fades
361             # mute edit track
362             # schedule unmuting at rec-start point - fade-in
363             # schedule muting at rec-end point - fade-out
364             }
365              
366             sub new_edit {
367              
368             # abort for many different reasons
369            
370             Audio::Nama::throw("You must use 'set_edit_points' before creating a new edit. Aborting."),
371 0 0   0     return unless @{$setup->{edit_points}};
  0            
372             my $overlap = grep {
373 0           my $fail;
374 0           my $rst = $_->rec_start_time;
375 0           my $ret = $_->rec_end_time;
376 0           my $nst = $setup->{edit_points}->[1];
377 0           my $net = $setup->{edit_points}->[2];
378 0           my $rst1 = d1($rst);
379 0           my $ret1 = d1($ret);
380 0           my $nst1 = d1($nst);
381 0           my $net1 = d1($net);
382 0 0 0       Audio::Nama::throw("New rec-start time $nst1 conflicts with Edit ",
383             $_->n, ": $rst1 < $nst1 < $ret1"), $fail++
384             if $rst < $nst and $nst < $ret;
385 0 0 0       Audio::Nama::throw("New rec-end time $net1 conflicts with Edit ",
386             $_->n, ": $rst1 < $net1 < $ret1"), $fail++
387             if $rst < $net and $net < $ret;
388 0 0 0       Audio::Nama::throw("New rec interval $nst1 - $net1 conflicts with Edit ",
389             $_->n, ": $rst1 - $ret1"), $fail++
390             if $nst < $rst and $ret < $net;
391 0           $fail
392 0           } grep{ $_->host_track eq $this_track->name}
  0            
393             values %Audio::Nama::Edit::by_name;
394 0 0         Audio::Nama::throw("Aborting."), return if $overlap;
395 0           my $name = $this_track->name;
396 0           my $editre = qr($name-v\d+-edit\d+);
397 0 0         Audio::Nama::throw("$name: editing of edits is not currently allowed."),
398             return if $name =~ /-v\d+-edit\d+/;
399             Audio::Nama::throw("$name: must be in PLAY mode.
400             Edits will be applied against current version"),
401             return unless $this_track->rec_status eq PLAY
402             or $this_track->rec_status eq REC and
403 0 0 0       grep{ /$editre/ } keys %Audio::Nama::Track::by_name;
  0   0        
404              
405             # create edit
406            
407 0           my $v = $this_track->monitor_version;
408 0           Audio::Nama::pager("$name: creating new edit against version $v");
409 0           my $edit = Audio::Nama::Edit->new(
410             host_track => $this_track->name,
411             host_version => $v,
412             );
413 0           $this_track->current_edit->{$v} = $edit->n;
414 0           $this_edit = $edit;
415 0           transfer_edit_points($edit);
416             #select_edit($this_edit->n);
417 0           edit_action('preview_edit_in');
418             }
419             {my %edit_actions =
420             (
421             record_edit => sub {
422             $this_edit->edit_track->set(rw => REC);
423             $this_edit->store_fades(std_host_fades(), edit_fades());
424             },
425             play_edit => sub {
426             $this_edit->edit_track->set(rw => PLAY);
427             $this_edit->store_fades(std_host_fades(), edit_fades());
428             },
429             preview_edit_in => sub {
430             $this_edit->edit_track->set(rw => OFF);
431             $this_edit->store_fades(std_host_fades());
432             },
433             preview_edit_out => sub {
434             $this_edit->edit_track->set(rw => OFF);
435             $this_edit->store_fades(reverse_host_fades());
436             },
437             );
438              
439             sub edit_action {
440 0     0     my $action = shift;
441 0 0         defined $this_edit or Audio::Nama::throw("Please select an edit and try again."), return;
442 0           set_edit_mode();
443 0           $this_edit->host_alias_track->set(rw => PLAY); # all
444 0           $edit_actions{$action}->();
445 0           request_setup();
446              
447             # TODO: looping
448             # my $is_setup = generate_setup();
449             # return unless $is_setup;
450             # if ($action !~ /record/){
451             # $mode->{loop_enable}++;
452             # @{$setup->{loop_endpoints}} = (0,$setup->{audio_length} - 0.05);
453             # # and transport_start()
454             # }
455             # connect_transport();
456             }
457             }
458              
459             sub end_edit_mode {
460              
461             # regenerate fades
462            
463 0     0     $mode->{offset_run} = 0;
464 0           $mode->{loop_enable} = 0;
465 0           disable_offset_run_mode();
466 0 0         $this_track = $this_edit->host if defined $this_edit;
467 0           undef $this_edit;
468 0           request_setup();
469             }
470             sub destroy_edit {
471 0 0   0     Audio::Nama::throw("no edit selected"), return unless $this_edit;
472 0           my $reply = $text->{term}->readline('destroy edit "'.$this_edit->edit_name.
473             qq(" and all its WAV files?? [n] ));
474 0 0         if ( $reply =~ /y/i ){
475 0           Audio::Nama::pager("permanently removing edit");
476 0           $this_edit->destroy;
477             }
478 0           $text->{term}->remove_history($text->{term}->where_history);
479 0           $this_track = $this_edit->host;
480 0           end_edit_mode();
481             }
482 0 0   0     sub set_edit_mode { $mode->{offset_run} = edit_mode_conditions() ? 1 : 0 }
483 0 0   0     sub edit_mode { $mode->{offset_run} and defined $this_edit}
484             sub edit_mode_conditions {
485 0 0   0     defined $this_edit or Audio::Nama::throw('No edit is defined'), return;
486 0 0         defined $this_edit->play_start_time or Audio::Nama::throw('No edit points defined'), return;
487 0 0         $this_edit->host_alias_track->rec_status eq PLAY
488             or Audio::Nama::throw('host alias track : ',$this_edit->host_alias,
489             " status must be PLAY"), return;
490              
491             # the following conditions should never be triggered
492            
493 0 0         $this_edit->host_alias_track->monitor_version == $this_edit->host_version
494             or die('host alias track: ',$this_edit->host_alias,
495             " must be set to version ",$this_edit->host_version), return
496             1;
497             }
498 0     0     sub reverse_host_fades { host_fades('in','out') }
499              
500 0     0     sub std_host_fades { host_fades('out','in') }
501              
502             sub host_fades {
503 0     0     my ($first,$second) = @_;
504             Audio::Nama::Fade->new( type => $first,
505             mark1 => $this_edit->rec_start_name,
506             duration => $config->{edit_crossfade_time},
507             relation => 'fade_from_mark',
508             track => $this_edit->host_alias,
509             ),
510             Audio::Nama::Fade->new( type => $second,
511             mark1 => $this_edit->rec_end_name,
512             duration => $config->{edit_crossfade_time},
513 0           relation => 'fade_from_mark',
514             track => $this_edit->host_alias,
515             ),
516             }
517             sub edit_fades {
518             Audio::Nama::Fade->new( type => 'in',
519             mark1 => $this_edit->rec_start_name,
520             duration => $config->{edit_crossfade_time},
521             relation => 'fade_from_mark',
522             track => $this_edit->edit_name,
523             ),
524             Audio::Nama::Fade->new( type => 'out',
525             mark1 => $this_edit->rec_end_name,
526             duration => $config->{edit_crossfade_time},
527 0     0     relation => 'fade_from_mark',
528             track => $this_edit->edit_name,
529             );
530             }
531             ### edit region computations
532             # pass $args hash with following fields:
533             #
534             ### track values
535             # trackname
536             # playat
537             # region_start
538             # region_end
539             # setup_length
540             #
541             ### edit values
542             # edit_play_start
543             # edit_play_end
544             #
545             ### dispatch tables
546             # playat_dispatch
547             # region_start_dispatch
548             # region_end_dispatch
549             #
550             ### output values
551             #
552             # new_playat
553             # new_region_start
554             # new_region_end
555              
556             sub region_start_dispatch {
557 0     0     my ($args, $key) = @_;
558             my %table = (
559              
560             out_of_bounds_near => "*",
561             out_of_bounds_far => "*",
562              
563             play_start_during_playat_delay => $args->{region_start},
564             no_region_play_start_during_playat_delay => 0,
565              
566             play_start_within_region
567             => $args->{region_start} + $args->{edit_play_start} - $args->{playat},
568             no_region_play_start_after_playat_delay
569             => $args->{region_start} + $args->{edit_play_start} - $args->{playat},
570 0           );
571 0           $table{$key}
572             }
573             sub playat_dispatch {
574 0     0     my ($args, $key) = @_;
575             my %table = (
576             out_of_bounds_near => "*",
577             out_of_bounds_far => "*",
578              
579             play_start_during_playat_delay => $args->{playat} - $args->{edit_play_start},
580             no_region_play_start_during_playat_delay
581             => $args->{playat} - $args->{edit_play_start},
582              
583 0           play_start_within_region => 0,
584             no_region_play_start_after_playat_delay => 0,
585             );
586 0           $table{$key}
587             }
588             sub region_end_dispatch {
589 0     0     my ($args, $key) = @_;
590             my %table = (
591             out_of_bounds_near => "*",
592             out_of_bounds_far => "*",
593              
594             play_start_during_playat_delay
595             => $args->{region_start} + $args->{edit_play_end} - $args->{playat},
596             no_region_play_start_during_playat_delay
597             => $args->{edit_play_end} - $args->{playat},
598              
599             play_start_within_region
600             => $args->{region_start} + $args->{edit_play_end} - $args->{playat},
601             no_region_play_start_after_playat_delay
602             => $args->{edit_play_end} - $args->{playat},
603 0           );
604 0           $table{$key}
605             }
606             sub new_playat {
607 0     0     my $args = shift;
608 0           playat_dispatch($args, edit_case($args));
609             }
610             sub new_region_start {
611 0     0     my $args = shift;
612 0           region_start_dispatch($args, edit_case($args));
613             }
614             sub new_region_end {
615 0     0     my $args = shift;
616 0           my $end = region_end_dispatch($args, edit_case($args));
617 0 0         return $end if $end eq '*';
618             $end < $args->{setup_length} ? $end : $args->{setup_length}
619 0 0         };
620             # the following value will always allow enough time
621             # to record the edit. it may be longer than the
622             # actual WAV file in some cases. (I doubt that
623             # will be a problem.)
624              
625             sub edit_case {
626 0     0     my $args = shift;
627              
628             # logic for no-region case
629            
630 0 0 0       if ( ! $args->{region_start} and ! $args->{region_end} )
    0 0        
631             {
632 0 0 0       if( $args->{edit_play_end} < $args->{playat})
    0          
    0          
    0          
633 0           { "out_of_bounds_near" }
634             elsif( $args->{edit_play_start} > $args->{playat} + $args->{setup_length})
635 0           { "out_of_bounds_far" }
636             elsif( $args->{edit_play_start} >= $args->{playat})
637 0           {"no_region_play_start_after_playat_delay"}
638             elsif( $args->{edit_play_start} < $args->{playat} and $args->{edit_play_end} > $args->{playat} )
639 0           { "no_region_play_start_during_playat_delay"}
640             }
641             # logic for region present case
642            
643             elsif ( defined $args->{region_start} and defined $args->{region_end} )
644             {
645 0 0 0       if ( $args->{edit_play_end} < $args->{playat})
    0          
    0          
    0          
646 0           { "out_of_bounds_near" }
647             elsif ( $args->{edit_play_start} > $args->{playat} + $args->{region_end} - $args->{region_start})
648 0           { "out_of_bounds_far" }
649             elsif ( $args->{edit_play_start} >= $args->{playat})
650 0           { "play_start_within_region"}
651             elsif ( $args->{edit_play_start} < $args->{playat} and $args->{playat} < $args->{edit_play_end})
652 0           { "play_start_during_playat_delay"}
653 0           else {carp "$args->{trackname}: fell through if-then"}
654             }
655 0           else { carp "$args->{trackname}: improperly defined region" }
656             }
657              
658             sub play_start_time {
659             defined $this_edit
660             ? $this_edit->play_start_time
661             : $setup->{offset_run}->{start_time} # zero unless offset run mode
662 0 0   0     }
663             sub play_end_time {
664             defined $this_edit
665             ? $this_edit->play_end_time
666             : $setup->{offset_run}->{end_time} # undef unless offset run mode
667 0 0   0     }
668             sub edit_vars {
669 0   0 0     my $edit = shift || $this_edit;
670 0 0         Audio::Nama::throw("edit is undefined"), return unless $edit;
671 0           my $track = $Audio::Nama::tn{$edit}->{host_track};
672             {
673 0           trackname => $track->name,
674             playat => $track->playat_time,
675             region_start => $track->region_start_time,
676             region_end => $track->region_end_time,
677             edit_play_start => $edit->play_start_time(),
678             edit_play_end => $edit->play_end_time(),
679             setup_length => $track->wav_length(),
680             }
681             }
682              
683             sub list_edits {
684             my @edit_data =
685 0           map{ s/^---//; s/...\s$//; $_ }
  0            
  0            
686 0           map{ $_->dump }
687 0     0     sort{$a->n <=> $b->n}
  0            
688             values %Audio::Nama::Edit::by_index;
689 0           Audio::Nama::pager(@edit_data);
690             }
691             sub explode_track {
692 0     0     my $track = shift;
693            
694             # quit if I am already a mix track
695              
696 0 0         Audio::Nama::throw($track->name,": I am already a mix track. I cannot explode!"),return
697             if $track->is_mix_track;
698              
699 0           my @versions = @{ $track->versions };
  0            
700              
701             # quit if I have only one version
702              
703 0 0         Audio::Nama::throw($track->name,": Only one version. Skipping."), return
704             if scalar @versions == 1;
705              
706 0           $track->busify;
707              
708 0           my $host = $track->name;
709 0           my @names = map{ "$host-v$_"} @versions;
  0            
710 0           my @exists = grep{ $Audio::Nama::tn{$_} } @names;
  0            
711 0 0         Audio::Nama::throw("@exists: tracks already exist. Aborting."), return if @exists;
712 0           my $current = cwd;
713 0           chdir this_wav_dir();
714 0           for my $i (@versions){
715              
716             # make a track
717              
718 0           my $name = "$host-v$i";
719 0           Audio::Nama::Track->new(
720             name => $name,
721             rw => MON,
722             group => $host,
723             );
724              
725             # symlink the WAV file we want
726              
727 0           symlink $track->targets->{$i}, "$name.wav";
728              
729              
730             }
731 0           chdir $current;
732             }
733              
734             sub select_edit {
735 0     0     my $n = shift;
736 0           my ($edit) = grep{ $_->n == $n } values %Audio::Nama::Edit::by_name;
  0            
737              
738             # check that conditions are met
739            
740 0 0         Audio::Nama::throw("Edit $n not found. Skipping."),return if ! $edit;
741 0 0         Audio::Nama::throw( qq(Edit $n applies to track "), $edit->host_track,
742             qq(" version ), $edit->host_version, ".
743             This does does not match the current monitor version (",
744             $edit->host->monitor_version,").
745             Set the correct version and try again."), return
746             if $edit->host->monitor_version != $edit->host_version;
747              
748             # select edit
749            
750 0           $this_edit = $edit;
751              
752             # turn on top-level bus and mix track
753            
754 0           $edit->host_bus->set(rw => REC);
755              
756 0           $edit->host->busify;
757              
758             # turn off all version level buses/mix_tracks
759            
760 0           map{ $tn{$_}->set(rw => OFF); # version mix tracks
  0            
761 0           $bn{$_}->set(rw => OFF); # version buses
762             } $this_edit->host_bus->tracks; # use same name for track/bus
763              
764             # turn on what we want
765            
766 0           $edit->version_bus->set(rw => REC);
767              
768 0           $edit->version_mix->busify;
769              
770 0           $edit->host_alias_track->set(rw => PLAY);
771              
772 0           $edit->edit_track->set(rw => PLAY);
773            
774 0           $this_track = $edit->host;
775             }
776             sub disable_edits {
777              
778 0 0   0     Audio::Nama::throw("Please select an edit and try again."), return
779             unless defined $this_edit;
780 0           my $edit = $this_edit;
781              
782 0           $edit->host_bus->set( rw => OFF);
783              
784 0           $edit->version_bus->set( rw => OFF);
785              
786             # reset host track
787            
788 0           $edit->host->unbusify;
789            
790             }
791             sub merge_edits {
792 0     0     my $edit = $this_edit;
793 0 0         Audio::Nama::throw("Please select an edit and try again."), return
794             unless defined $edit;
795 0 0         Audio::Nama::throw($edit->host_alias, ": track must be PLAY status. Aborting."), return
796             unless $edit->host_alias_track->rec_status eq PLAY;
797 0 0         Audio::Nama::throw("Use exit_edit_mode and try again."), return if edit_mode();
798              
799             # create merge message
800 0           my $v = $edit->host_version;
801             my %edits =
802 0           map{ my ($edit) = $tn{$_}->name =~ /edit(\d+)$/;
803 0           my $ver = $tn{$_}->monitor_version;
804 0           $edit => $ver
805 0 0         } grep{ $tn{$_}->name =~ /edit\d+$/ and $tn{$_}->rec_status eq PLAY}
  0            
806             $edit->version_bus->tracks;
807             my $msg = "merges ".$edit->host_track."_$v.wav w/edits ".
808 0           join " ",map{$_."v$edits{$_}"} sort{$a<=>$b} keys %edits;
  0            
  0            
809             # merges mic_1.wav w/mic-v1-edits 1_2 2_1
810            
811 0           Audio::Nama::pager($msg);
812              
813             # cache at version_mix level
814            
815 0           my $output_wav = cache_track($edit->version_mix);
816              
817             # promote to host track
818              
819 0           my $new_version = $edit->host->last + 1;
820 0           add_system_version_comment($edit->host, $new_version, $msg);
821 0           add_system_version_comment($edit->version_mix, $edit->version_mix->last, $msg);
822 0           my $old = cwd();
823 0           chdir this_wav_dir();
824 0           my $new_host_wav = $edit->host_track . "_" . $new_version . ".wav";
825 0           symlink $output_wav, $new_host_wav;
826              
827 0           $edit->host->set(version => undef); # default to latest
828 0           $edit->host->{version_comment}{$new_version}{system} = $msg;
829 0           chdir $old;
830 0           disable_edits();
831 0           $this_track = $edit->host;
832            
833             }
834             # offset recording
835              
836             # Note that although we use ->shifted_* methods, all are
837             # executed outside of edit mode, so we get unadjusted values.
838              
839             sub setup_length {
840 0     0     my $setup_length;
841 0 0         map{ my $l = $_->shifted_length; $setup_length = $l if $l > $setup_length }
  0            
842 0           grep{ $_-> rec_status eq PLAY }
  0            
843             Audio::Nama::ChainSetup::engine_tracks();
844 0           $setup_length
845             }
846             sub set_offset_run_mark {
847 0 0   0     Audio::Nama::throw("This function not available in edit mode. Aborting."),
848             return if edit_mode();
849 0           my $markname = shift;
850            
851 0           $setup->{offset_run}->{start_time} = $Audio::Nama::Mark::by_name{$markname}->time;
852 0           $setup->{offset_run}->{end_time} = setup_length();
853 0           $setup->{offset_run}->{mark} = $markname;
854 0           enable_offset_run_mode();
855 0           request_setup();
856             }
857             sub clear_offset_run_vars {
858 0     0     $setup->{offset_run}->{start_time} = 0;
859 0           $setup->{offset_run}->{end_time} = undef;
860 0           $setup->{offset_run}->{mark} = undef;
861             }
862             sub enable_offset_run_mode {
863 0     0     undef $this_edit;
864 0           $mode->{offset_run}++
865             }
866             sub disable_offset_run_mode {
867 0     0     undef $mode->{offset_run};
868 0           clear_offset_run_vars();
869 0           Audio::Nama::request_setup();
870             }
871 0 0   0     sub is_offset_run_mode { $mode->{offset_run} and ! defined $this_edit }
872            
873             sub select_edit_track {
874 0     0     my $track_selector_method = shift;
875 0 0         Audio::Nama::throw("You need to select an edit first (list_edits, select_edit)\n"),
876             return unless defined $this_edit;
877 0           $this_track = $this_edit->$track_selector_method;
878 0           process_command('show_track');
879             }
880              
881             } # end package
882              
883             1;
884              
885             __END__