Mercurial > hg > midifile
changeset 2:1a4000271db6 midifile
* Add note-column calculation script
| author | cannam |
|---|---|
| date | Thu, 11 Feb 2010 12:26:07 +0000 |
| parents | 3e65e0344413 |
| children | 5abb6a523d27 |
| files | midisort.pl |
| diffstat | 1 files changed, 120 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/midisort.pl Thu Feb 11 12:26:07 2010 +0000 @@ -0,0 +1,120 @@ +use strict; + +# read the output from midifile, sort it into "note column" groups + +my $timebase = 120; +my $beatsperbar = 3; + +sub fmtm { # function to convert time to bar number + int($_[0] / ($timebase * $beatsperbar)) + 1; +} +sub fmt { # function to convert time to beat number + my $t = $_[0] / $timebase; + while ($t >= $beatsperbar) { $t -= $beatsperbar; } + $t + 1; +} +sub fmtd { # function to convert duration to beat count + $_[0] / $timebase; +} + +my %noteons; +my %noteoffs; +my %channels; +my %grouptimes; + +while (<>) { + + my ($t,$c,$d,$p); + + # split events into time, channel, duration, pitch + if (/^(\d+): Note: channel (\d+) duration (\d+) pitch (\d+)/) { + $t = $1; + $c = $2; + $d = $3; + $p = $4; + } else { + next; + } + + # record note-on times, plus all active channels + $noteons{$t}{$c}{$d}{$p}++; + $channels{$c}++; + + # start times of note groups: whenever a note starts or ends + $grouptimes{$t}++; + $grouptimes{$t + $d}++; +} + +my $prevt; +my %groupdurs; + +# work out the duration of each note-group +for my $t (sort { $a <=> $b } keys %grouptimes) { # sort group starts numerically + if (defined $prevt) { + # duration of the previous note-group is that from its start + # time to that of the current one + $groupdurs{$prevt} = $t - $prevt; + } + $prevt = $t; +} + +my $n = 0; + +for my $t (sort { $a <=> $b } keys %groupdurs) { + + printf "NOTEGROUP:%d Bar:%d Beat:%.1f Dur:%.1f", + $n, fmtm($t), fmt($t), fmtd($groupdurs{$t}); + + # within this group we want to output a pitch number for every + # "known" channel, i.e. everything in keys %channels. these + # pitches may be based on notes starting at this time (via noteons + # hash) or notes that have started previously but not yet finished + # (via noteoffs hash) + + print " Midi: ["; + + my @pitches; # array of all pitches (ordered by channel) in this group + + for my $ot (keys %noteoffs) { + # prune notes that have ended already or are ending now + if ($ot le $t) { + delete $noteoffs{$ot}; + } + } + + for my $c (sort { $a <=> $b } keys %channels) { + + my @here; # array of all pitches on this channel + + for my $d (keys %{$noteons{$t}{$c}}) { + for my $p (keys %{$noteons{$t}{$c}{$d}}) { + # add notes that begin at this time, indexed by end time + $noteoffs{$t + $d}{$c}{$p}++; + } + } + + for my $ot (keys %noteoffs) { + # note(s) still sounding, add to group if in channel + for my $p (keys %{$noteoffs{$ot}{$c}}) { + push @here, $p; + } + } + + if (@here > 0) { + # if we have any notes on this channel in this group, + # prepare to print them separated by spaces + push @pitches, (join " ", @here); + } else { + # otherwise prepare to print 'rest' + push @pitches, "'rest'"; + } + } + + # now print the channel note lists separated by comma + print join ", ", @pitches; + + print "]\n"; + + ++$n; +} +
