Revision 2:1a4000271db6

View differences:

midisort.pl
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

  

Also available in: Unified diff