+ elsif($cmd eq 'retrack')
+ {
+ my $tracks = $opus->tracks_r();
+ my @newtracks = ();
+ for(0..@$tracks-1)
+ {
+ for(abstime $tracks->[$_]->events())
+ {
+ my $p = $chanpos{$_->[0]};
+ if(defined $p)
+ {
+ my $c = $_->[$p] + 1;
+ push @{$newtracks[$c]}, $_;
+ }
+ else
+ {
+ push @{$newtracks[0]}, $_;
+ }
+ }
+ }
+ $opus->tracks_r([map { ($_ && @$_) ? MIDI::Track->new({ events => [reltime @$_] }) : () } @newtracks]);
+ }
+ elsif($cmd eq 'program')
+ {
+ my $tracks = $opus->tracks_r();
+ my ($track, $channel, $program) = @arg;
+ for(($track eq '*') ? (0..@$tracks-1) : $track)
+ {
+ my @events = ();
+ my $added = 0;
+ for(abstime $tracks->[$_]->events())
+ {
+ my $p = $chanpos{$_->[0]};
+ if(defined $p)
+ {
+ my $c = $_->[$p] + 1;
+ if($channel eq '*' || $c == $channel)
+ {
+ next
+ if $_->[0] eq 'patch_change';
+ if(!$added)
+ {
+ push @events, ['patch_change', $_->[1], $c-1, $program-1]
+ if $program;
+ $added = 1;
+ }
+ }
+ }
+ push @events, $_;
+ }
+ $tracks->[$_]->events_r([reltime @events]);
+ }
+ }
+ elsif($cmd eq 'transpose')
+ {
+ my $tracks = $opus->tracks_r();
+ my ($track, $channel, $delta) = @arg;
+ for(($track eq '*') ? (0..@$tracks-1) : $track)
+ {
+ for($tracks->[$_]->events())
+ {
+ my $p = $chanpos{$_->[0]};
+ if(defined $p)
+ {
+ my $c = $_->[$p] + 1;
+ if($channel eq '*' || $c == $channel)
+ {
+ if($_->[0] eq 'note_on' || $_->[0] eq 'note_off')
+ {
+ $_->[3] += $delta;
+ }
+ }
+ }
+ }
+ }
+ }
+ elsif($cmd eq 'channel')
+ {
+ my $tracks = $opus->tracks_r();
+ my ($track, %chanmap) = @arg;
+ for(($track eq '*') ? (0..@$tracks-1) : $track)
+ {
+ my @events = ();
+ for(abstime $tracks->[$_]->events())
+ {
+ my $p = $chanpos{$_->[0]};
+ if(defined $p)
+ {
+ my $c = $_->[$p] + 1;
+ $c = $chanmap{$c} // $chanmap{'*'} // $c;
+ next
+ if $c == 0; # kill by setting channel to 0
+ $_->[$p] = $c - 1;
+ }
+ push @events, $_;
+ }
+ $tracks->[$_]->events_r([reltime @events]);
+ }
+ }
+ elsif($cmd eq 'percussion')
+ {
+ my $tracks = $opus->tracks_r();
+ my ($track, $channel, %map) = @arg;
+ for(($track eq '*') ? (0..@$tracks-1) : $track)
+ {
+ my @events = ();
+ for(abstime $tracks->[$_]->events())
+ {
+ my $p = $chanpos{$_->[0]};
+ if(defined $p)
+ {
+ my $c = $_->[$p] + 1;
+ if($channel eq '*' || $c == $channel)
+ {
+ if($_->[0] eq 'note_on' || $_->[0] eq 'note_off')
+ {
+ if(length $map{$_->[3]})
+ {
+ $_->[3] = $map{$_->[3]};
+ }
+ elsif(exists $map{$_->[3]})
+ {
+ next;
+ }
+ }
+ }
+ }
+ push @events, $_;
+ }
+ $tracks->[$_]->events_r([reltime @events]);
+ }
+ }