Re: MIDI
[email protected] ("Sean M. Burke") Tue, 12 Dec 2000 22:23:34 -0700
| Newsgroups | perl.midi |
|---|---|
| Message-ID | <[email protected]> |
At 11:18 PM 2000-12-10 +0100, Elettrone Gravitante wrote:
>>What sorts of changes are you planning to make between the source file
>>and the new file? That would probably dictate what kind of data
>>structure to use.
>[...]I think that use MIDI::Score data structure is the best
>solution but, how can I import a score from a Midi file?[...]
Hm, I don't completely understand the transform you want to apply to
notes, altho I understand that it's a function of each note's
note-number and start-time.
I've written some example code that modifies a score structure's
notes' start-times and note-numbers; hopefully you can make sense
of it, put your transform in place of mine.
I don't get asked that much about modifying MIDI files, so maybe the
MIDI Perl list'd like to see this. I'll copy them on this message.
OK, first to make a MIDI file, so we all know what we're talking about:
use MIDI::Simple;
new_score;
patch_change 1, 8; # set Channel 1 to Patch 8 = Celesta
noop c1, f, o5; # Setup
# Now play
n qn, Cs; n F; n Ds; n hn, Gs_d1;
n qn, Cs; n Ds; n F; n hn, Cs;
n qn, F; n Cs; n Ds; n hn, Gs_d1;
n qn, Gs_d1; n Ds; n F; n hn, Cs;
write_score 'c1.mid';
# that's the Westminster Chimes. Tired of it yet? SING ALONG!
# Lord through this hour/ be Thou our guide
# so, by Thy power/ no foot shall slide
Now to take an arbitrary track (well, the one track from the
file that the above program generated), and mess with it:
##### New program #####
use strict;
use MIDI;
my $o = MIDI::Opus->new(
{ 'from_file' => 'c1.mid' } # read in whatever file
);
my(@t) = $o->tracks;
die "Wrong number of tracks (" . scalar(@t) . ")" unless @t == 1;
# For example's sake, deal with only one track. In practice, tho,
# we could iterate over each track, changing each.
my $t = $t[0];
my $score =
MIDI::Score::events_r_to_score_r( $t->events_r );
# And if you wanna look at it:
MIDI::Score::dump_score( $score );
#...That'll print:
# @notes = ( # 18 notes...
# ['text_event', 0, 'make1.pl at Tue Dec 12 21:05:52 2000'],
# ['patch_change', 0, 1, 8],
# ['note', 0, 96, 1, 61, 96],
# ['note', 96, 96, 1, 65, 96],
# ['note', 192, 96, 1, 63, 96],
# ['note', 288, 192, 1, 56, 96],
# ['note', 480, 96, 1, 61, 96],
# ['note', 576, 96, 1, 63, 96],
# ['note', 672, 96, 1, 65, 96],
# ['note', 768, 192, 1, 61, 96],
# ['note', 960, 96, 1, 65, 96],
# ['note', 1056, 96, 1, 61, 96],
# ['note', 1152, 96, 1, 63, 96],
# ['note', 1248, 192, 1, 56, 96],
# ['note', 1440, 96, 1, 56, 96],
# ['note', 1536, 96, 1, 63, 96],
# ['note', 1632, 96, 1, 65, 96],
# ['note', 1728, 192, 1, 61, 96],
# );
# See MIDI::Score for the info on score structures.
# The short story, tho, is:
# ('note', starttime, duration, channel, note, velocity)
# 0 1 2 3 4 5
my @more;
foreach my $n (@$score) {
next unless $n->[0] eq 'note'; # skip non-notes.
# Change the note-number somehow. For example:
$n->[4] += 10; # raise 5 steps
# Change the time somehow. For example:
$n->[1] = int( ($n->[1] ** 1.5) / 50 );
# molto allegro, ma rallentando presto
# Or even make more notes -- just as long as
# we don't change the array we're in the middle of
# iterating over:
push @more, [@$n]; # make a copy of that note
# and alter it...
$more[-1][4] += 3 + int(rand 1.4); # note-number
$more[-1][1] += 45 - int(rand 90); # and start-time
$more[-1][1] = 0 if $more[-1][1] < 0;
# Having a negative start-time will be a fatal error
# later on. If you can't just force things to 0,
# then figure out the lowest negative number (in the
# track? in the file?), then go and subtract that much
# from all the notes' start time -- i.e., moving everything
# up, so the earliest thing starts now at time 0.
}
push @$score, @more; # if you made additional notes
print "\n\nScore after change:\n";
MIDI::Score::dump_score( $score );
# Yes, it'll be a bit out-of-order. But score_r_to_events_r
# will still make good sense of it.
# Now, make an events structure, and put it back in the track
# we got it from.
$t->events_r(
scalar MIDI::Score::score_r_to_events_r( $score )
);
# Now write out the opus containing that track.
$o->write_to_file('c2.mid');
exit;
--
Sean M. Burke [email protected] http://www.spinn.net/~sburke/