use strict; use warnings; use MIDI; use XML::Simple; use Data::Dumper; my $xml = XMLin(\*DATA); my $song = $xml->{SONG}; my @note = @{ $song->{NOTE} }; my $title = $song->{title} || 'unknown'; my $author = $song->{author} || 'unknown'; my (@event, $pitch, $duration, $velocity, $instrument); for my $note (@note) { # check for patch changes if (exists $note->{instrument}) { $instrument = $MIDI::patch2number{$note->{instrument}} || 1; push @event,[patch_change => 0, 1, $instrument]; } $pitch = scrub_pitch($note->{pitch},$pitch); $duration = ($note->{duration} || 1) * 96; $velocity = ($pitch eq 'rest') ? 0 : 96; # hacked silence # add note_on/note_off pair push @event,[note_on => 0,1,$MIDI::note2number{$pitch},$velocity]; push @event,[note_off => $duration,1,$MIDI::note2number{$pitch},0]; } my $opus = MIDI::Opus->new({ format => 0, ticks => $song->{bpm}, tracks => [ MIDI::Track->new({ events => [ [text_event => 0, "Title: $title"], [text_event => 0, "Auhtor: $author"], @event, ] })], }); $opus->write_to_file($song->{name}.'.mid'); # this transforms C into C4 and C:5 into C5 (etc.) sub scrub_pitch { my ($next,$prev) = @_; return $prev unless $next; my ($note,$octave) = split(':',$next,2); return $note if lc($note) eq 'rest'; $octave ||= 4; return "$note$octave"; } __DATA__