X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=misc%2Ftools%2Fmidichannels.pl;h=4df6978be9bb2cb790d4d5523d80feed46b7979d;hb=8a946096009455bec30aa79c51b4c2877646a779;hp=5cfe188177031a99e63c0f85a3eec7c9cf8a4cd7;hpb=e5832bf2ecac60a672a29c85cd8dec341a215e1c;p=xonotic%2Fxonotic.git diff --git a/misc/tools/midichannels.pl b/misc/tools/midichannels.pl old mode 100644 new mode 100755 index 5cfe1881..4df6978b --- a/misc/tools/midichannels.pl +++ b/misc/tools/midichannels.pl @@ -5,7 +5,7 @@ use warnings; use MIDI; use MIDI::Opus; -my ($filename) = @ARGV; +my ($filename, @others) = @ARGV; my $opus = MIDI::Opus->new({from_file => $filename}); my %chanpos = ( @@ -18,13 +18,61 @@ my %chanpos = ( pitch_wheel_change => 2 ); +my %isclean = ( + set_tempo => sub { 1; }, + note_off => sub { 1; }, + note_on => sub { 1; }, + control_change => sub { $_[3] == 64; }, +); + +sub abstime(@) +{ + my $t = 0; + return map { [$_->[0], $t += $_->[1], @{$_}[2..(@$_-1)]]; } @_; +} + +sub reltime(@) +{ + my $t = 0; + return map { my $tsave = $t; $t = $_->[1]; [$_->[0], $t - $tsave, @{$_}[2..(@$_-1)]]; } @_; +} + +sub clean(@) +{ + return reltime grep { ($isclean{$_->[0]} // sub { 0; })->(@$_) } abstime @_; +} + +for(@others) +{ + my $opus2 = MIDI::Opus->new({from_file => $_}); + if($opus2->ticks() != $opus->ticks()) + { + my $tickfactor = $opus->ticks() / $opus2->ticks(); + for($opus2->tracks()) + { + $_->events(reltime map { $_->[1] = int($_->[1] * $tickfactor + 0.5); $_; } abstime $_->events()); + } + } + $opus->tracks($opus->tracks(), $opus2->tracks()); +} + while() { chomp; my @arg = split /\s+/, $_; my $cmd = shift @arg; print "Executing: $cmd @arg\n"; - if($cmd eq 'ticks') + if($cmd eq 'clean') + { + my $tracks = $opus->tracks_r(); + $tracks->[$_]->events_r([clean($tracks->[$_]->events())]) + for 0..@$tracks-1; + } + elsif($cmd eq 'dump') + { + print $opus->dump({ dump_tracks => 1 }); + } + elsif($cmd eq 'ticks') { if(@arg) { @@ -39,13 +87,122 @@ while() { print "haha, very funny\n"; } + 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 ($channel, $program) = @arg; + for(0..@$tracks-1) + { + my @events = (); + my $added = 0; + for(abstime $tracks->[$_]->events()) + { + my $p = $chanpos{$_->[0]}; + if(defined $p) + { + my $c = $_->[$p] + 1; + if($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 'percussion') + { + my $tracks = $opus->tracks_r(); + my %map = @arg; + for(0..@$tracks-1) + { + my @events = (); + for(abstime $tracks->[$_]->events()) + { + my $p = $chanpos{$_->[0]}; + if(defined $p) + { + my $c = $_->[$p] + 1; + if($c == 10) + { + 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]); + } + } elsif($cmd eq 'tracks') { my $tracks = $opus->tracks_r(); if(@arg) { - my %taken = (0 => 1); - my @t = ($tracks->[0]); + my %taken = (); + my @t = (); my $force = 0; for(@arg) { @@ -61,7 +218,7 @@ while() } else { - for(1..@$tracks-1) + for(0..@$tracks-1) { print "Track $_:"; my $name = undef; @@ -69,22 +226,28 @@ while() my $notes = 0; my %notehash = (); my $t = 0; + my $events = 0; for($tracks->[$_]->events()) { + ++$events; $_->[0] = 'note_off' if $_->[0] eq 'note_on' and $_->[4] == 0; $t += $_->[1]; my $p = $chanpos{$_->[0]}; if(defined $p) { my $c = $_->[$p] + 1; - ++$channels{$c}; + $channels{$c} //= {}; + if($_->[0] eq 'patch_change') + { + ++$channels{$c}{$_->[3]}; + } } ++$notes if $_->[0] eq 'note_on'; $notehash{$_->[2]}{$_->[3]} = $t if $_->[0] eq 'note_on'; $notehash{$_->[2]}{$_->[3]} = undef if $_->[0] eq 'note_off'; $name = $_->[2] if $_->[0] eq 'track_name'; } - my $channels = join " ", sort keys %channels; + my $channels = join " ", map { sprintf "%s(%s)", $_, join ",", sort { $a <=> $b } keys %{$channels{$_}} } sort { $a <=> $b } keys %channels; my @stuck = (); while(my ($k1, $v1) = each %notehash) { @@ -96,6 +259,7 @@ while() } print " $name" if defined $name; print " (channel $channels)" if $channels ne ""; + print " ($events events)" if $events; print " ($notes notes)" if $notes; print " (notes @stuck stuck)" if @stuck; print "\n"; @@ -108,7 +272,15 @@ while() } else { - print "Unknown command, allowed commands: ticks, tricks, tracks, save\n"; + print "Unknown command, allowed commands:\n"; + print " clean\n"; + print " dump\n"; + print " ticks [value]\n"; + print " retrack\n"; + print " program \n"; + print " transpose \n"; + print " percussion [ ...]\n"; + print " tracks [trackno] [trackno] ...\n"; } print "Done with: $cmd @arg\n"; }