#!/usr/bin/perl -w use strict; use MIDI::Simple; # Here are our chord values my %chords = ( major => [0, 4, 7, 12], minor => [0, 3, 7, 12], major7 => [0, 4, 7, 11], minor7 => [0, 3, 7, 10], minor75 => [0, 3, 6, 10], dim => [0, 3, 6, 9], dom7 => [0, 4, 7, 10], major9 => [0, 4, 7, 11, 14], minor9 => [0, 3, 7, 10, 14], ); # the notes to be "chordified" and durations for our "score" # you will notice that this is the opposite notation taken by MIDI::Simple's n procedure # This is to make it extremely simple to "parse" # in this case, the tune is Westminster Chimes my @notes = ( ['Cs4', 'qn'], ['F4'], ['Ds4'], ['Gs3', 'hn'], ['Cs4', 'qn'], ['Ds4'], ['F4'], ['Cs4', 'hn'], ['F4', 'qn'], ['Cs4'], ['Ds4'], ['Gs3', 'hn'], ['Gs3', 'qn'], ['Ds4'], ['F4'], ['Cs4', 'hn'], ); # create the "score" in a major chord my @score = chordify(\@notes, $chords{major}); # this the text_events we will add to the MIDI my @text = ( 'http://www.ely.anglican.org/parishes/camgsm/bells/chimes.html', 'Lord through this hour/ be Thou our guide', 'so, by Thy power/ no foot shall slide' ); # Setup the MIDI score and write to file MakeMIDI(96, 127, 4, 1, 14, 'westminster_chimes.mid', \@text, \@score); sub MakeMIDI { # MakeMIDI(QNM, VOLUME, OCTAVE, CHANNEL, PATCH, FILENAME, TEXT_EVENTS, SCORE); # Take a "score" of notes and write it out as a MIDI file # $qnm is quarter notes per minute # $volume is numeric or text # $octave, $channel, $patch are numeric # $filename is the complete filepath for the output file # $text is a reference to an array of text events # $score is a reference to my version of a score array # example of a valid "score array" # my @score = ( # [ 'qn', 'C5' ], # [ 'E5' ], # [ 'en', 'F5', 'A5' ], # [ 'G5' ], # [ 'wn', 'rest' ], # ); my ($qnm, $volume, $octave, $channel, $patch, $filename, $text, $score) = @_; my $tempo = int(60_000_000/$qnm); my @text = @$text; my @score = @$score; new_score; if (@text > 0) { for my $event (@text) { text_event $event; } } set_tempo $tempo; patch_change $channel, $patch; $channel = 'c'.$channel; $octave = 'o'.$octave; noop $channel, $volume, $octave; # Setup for my $line (@score) { n @$line; } write_score $filename; } sub chord { # chord(NOTE, CHORD); # NOTE is the base note of the chord # CHORD is a reference to the chord array # uses MIDI::Simple's interval procedure my ($note, $chord) = @_; return interval $chord, $note; } sub chordify { # chordify(NOTES, CHORD); # nice name, huh? # Take a "score" of notes and return a "score" of chords # NOTES is a reference to an array of notes to "chordify" # CHORD is a reference to the chord array # returns my version of a "score" # an AoA containing duration and notes... my ($notes, $chord) = @_; my @notes = @$notes; my ($dur, @score); for my $line (@notes) { my $note; ($note, $dur) = @$line; my @chord = chord($note, $chord); $dur ? push @score, [$dur, @chord]: push @score, [@chord]; } return @score; }