-
Notifications
You must be signed in to change notification settings - Fork 5
/
event-horizon
118 lines (87 loc) · 2.74 KB
/
event-horizon
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#!/usr/bin/env perl
# https://www.youtube.com/watch?v=9g2WVYjDyeU <- Audio rendering
use strict;
use warnings;
use MIDI::Util qw(setup_score set_chan_patch);
use Music::ScaleNote;
use Music::Chord::Namer qw/ chordname /;
my $max = shift || 4;
my @tnotes = qw( C4 E4 G4 ); # Initial and final chord
my $bnote = 'C3'; # Final bass note
my @bnotes;
my $trebp = 0; # Treble patch
my $bassp = 42; # Bass patch
my $half = 'hn';
my $whole = 'wn';
my $score = setup_score( lead_in => 0, bpm => 20 );
# Instantiate a scale-note manipulator
my $msn = Music::ScaleNote->new(
scale_note => 'C',
scale_name => 'pentatonic',
note_format => 'midi',
# verbose => 1,
);
$score->synch(
\&top,
\&bottom,
);
$score->write_score("$0.mid");
sub top {
set_chan_patch( $score, 0, $trebp );
my @chord = @tnotes;
for my $i ( 1 .. $max ) {
# Return to the initial chord
# @chord = @tnotes unless $i % 4;
# Find the likely chord name
my @notes = @chord;
$_ =~ s/\d//g for @notes;
warn "$i. @notes = ", scalar( chordname(@notes) ), "\n";
# Update the bass notes with the bottom-most note of the chord
push @bnotes, $notes[0] . 3;
# Add the note and a rest to the score
$score->n( $whole, @chord );
$score->r($half);
# Mutate the chord!
@chord = mutate( $msn, 4, 5, @chord );
}
# Add the initial chord to the end of the score
$score->n( $whole, @tnotes );
push @bnotes, $bnote;
}
sub mutate {
my ( $msn, $lowerb, $upperb, @chord ) = @_;
# Get two of the chord notes to change
my $roll1 = $chord[ int rand @chord ];
my $roll2 = $chord[ int rand @chord ];
my @mutated;
# For each note of the chord...
for my $n0 ( @chord ) {
my $n = $n0;
# If it is one of the two changes...
if ( $n eq $roll1 || $n eq $roll2 ) {
( my $octave = $n ) =~ s/^.+(-?\d+)$/$1/;
my $note;
# Get a new note within the lower and upper bounds
while ( !$note || $octave < $lowerb || $octave > $upperb ) {
$note = $msn->get_offset(
note_name => $n0,
offset => $octave < $lowerb ? 1 : $octave > $upperb ? -1 : int rand 2 ? 1 : -1,
);
$n = $note->format('midi');
# Get the octave to determine if we need to loop again
( $octave = $n ) =~ s/^.+(-?\d+)$/$1/;
}
}
push @mutated, $n;
}
return @mutated;
}
sub bottom {
set_chan_patch( $score, 1, $bassp );
# $score->Volume(90);
# Add each bass note to the score
for my $note ( @bnotes ) {
$score->n( $whole, $note );
$score->r($half);
}
}