To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Revision:

root / midisort.pl @ 2:1a4000271db6

History | View | Annotate | Download (2.79 KB)

1
use strict;
2

    
3
# read the output from midifile, sort it into "note column" groups
4

    
5
my $timebase = 120;
6
my $beatsperbar = 3;
7

    
8
sub fmtm { # function to convert time to bar number
9
    int($_[0] / ($timebase * $beatsperbar)) + 1;
10
}
11
sub fmt { # function to convert time to beat number
12
    my $t = $_[0] / $timebase;
13
    while ($t >= $beatsperbar) { $t -= $beatsperbar; }
14
    $t + 1;
15
}
16
sub fmtd { # function to convert duration to beat count
17
    $_[0] / $timebase;
18
}
19

    
20
my %noteons;
21
my %noteoffs;
22
my %channels;
23
my %grouptimes;
24

    
25
while (<>) {
26

    
27
    my ($t,$c,$d,$p);
28

    
29
    # split events into time, channel, duration, pitch
30
    if (/^(\d+): Note: channel (\d+) duration (\d+) pitch (\d+)/) {
31
	$t = $1;
32
	$c = $2;
33
	$d = $3;
34
	$p = $4;
35
    } else {
36
	next;
37
    }
38

    
39
    # record note-on times, plus all active channels
40
    $noteons{$t}{$c}{$d}{$p}++;
41
    $channels{$c}++;
42

    
43
    # start times of note groups: whenever a note starts or ends
44
    $grouptimes{$t}++;
45
    $grouptimes{$t + $d}++;
46
}
47

    
48
my $prevt;
49
my %groupdurs;
50

    
51
# work out the duration of each note-group
52
for my $t (sort { $a <=> $b } keys %grouptimes) { # sort group starts numerically
53
    if (defined $prevt) {
54
	# duration of the previous note-group is that from its start
55
	# time to that of the current one
56
	$groupdurs{$prevt} = $t - $prevt;
57
    }
58
    $prevt = $t;
59
}
60

    
61
my $n = 0;
62

    
63
for my $t (sort { $a <=> $b } keys %groupdurs) {
64

    
65
    printf "NOTEGROUP:%d  Bar:%d  Beat:%.1f  Dur:%.1f",
66
	    $n, fmtm($t), fmt($t), fmtd($groupdurs{$t});
67

    
68
    # within this group we want to output a pitch number for every
69
    # "known" channel, i.e. everything in keys %channels.  these
70
    # pitches may be based on notes starting at this time (via noteons
71
    # hash) or notes that have started previously but not yet finished
72
    # (via noteoffs hash)
73

    
74
    print "  Midi: [";
75

    
76
    my @pitches; # array of all pitches (ordered by channel) in this group
77

    
78
    for my $ot (keys %noteoffs) {
79
	# prune notes that have ended already or are ending now
80
	if ($ot le $t) {
81
	    delete $noteoffs{$ot};
82
	}
83
    }
84

    
85
    for my $c (sort { $a <=> $b } keys %channels) {
86

    
87
	my @here; # array of all pitches on this channel
88

    
89
	for my $d (keys %{$noteons{$t}{$c}}) {
90
	    for my $p (keys %{$noteons{$t}{$c}{$d}}) {
91
		# add notes that begin at this time, indexed by end time
92
		$noteoffs{$t + $d}{$c}{$p}++;
93
	    }
94
	}
95

    
96
	for my $ot (keys %noteoffs) {
97
	    # note(s) still sounding, add to group if in channel
98
	    for my $p (keys %{$noteoffs{$ot}{$c}}) {
99
		push @here, $p;
100
	    }
101
	}
102

    
103
	if (@here > 0) {
104
	    # if we have any notes on this channel in this group,
105
	    # prepare to print them separated by spaces
106
	    push @pitches, (join " ", @here);
107
	} else {
108
	    # otherwise prepare to print 'rest'
109
	    push @pitches, "'rest'";
110
	}
111
    }
112

    
113
    # now print the channel note lists separated by comma
114
    print join ", ", @pitches;
115

    
116
    print "]\n";
117

    
118
    ++$n;
119
}
120