forked from ology/Music
-
Notifications
You must be signed in to change notification settings - Fork 0
/
random-selection
98 lines (79 loc) · 2.35 KB
/
random-selection
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
#!usr/bin/env perl
use strict;
use warnings;
use lib '/Users/gene/sandbox/MIDI-Util/lib';
use MIDI::Util qw(setup_score);
use MIDI::Simple;
use Music::AtonalUtil;
use Text::Levenshtein qw(distance);
my $transform = shift || ''; # Can be: invert, retrograde, rotate, transpose
my $parameter = shift || 0; # For: invert, rotate, transpose
my $distance = shift || 0; # Edits needed to transform subsequent phrases
my $phrase = shift || 4; # Number of notes in a phrase
my @notes = qw( C D E F G A B );
my @population = 0 .. @notes - 1;
# Choose a random populatioin sample
my @sample;
for my $i ( 1 .. $phrase ) {
push @sample, $population[ int rand @population ];
}
# Set the "previous" phrase
my $previous = join '', @sample;
# Add a L-distance constrained phrase to the sample
my @constrain;
for ( 1 ) {
for my $i ( 1 .. $phrase ) {
push @constrain, $population[ int rand @population ];
}
my $current = join '', @constrain;
my $d = distance( $previous, $current );
if ( $d == $distance ) {
# Update with the added constrained sample
@sample = ( @sample, @constrain );
}
else {
@constrain = ();
redo;
}
}
# Transform the sample, if requested
@sample = transform( $transform, $parameter, @sample ) if $transform;
# Process the MIDI...
my %name = int2name(@notes);
my @pitches = map { $name{$_} } @sample;
print "@pitches\n";
my $score = setup_score( patch => 0 );
$score->n( 'qn', $_ ) for @pitches;
$score->write_score( $0 . '.mid' );
sub transform {
my ( $transform, $parameter, @phrase ) = @_;
my $atu = Music::AtonalUtil->new;
my $p;
if ( $transform eq 'invert' ) {
$p = $atu->invert( $parameter, \@phrase );
}
elsif ( $transform eq 'retrograde' ) {
$p = $atu->retrograde(@phrase);
}
elsif ( $transform eq 'rotate' ) {
$p = $atu->rotate( $parameter, \@phrase );
}
elsif ( $transform eq 'transpose' ) {
$p = $atu->transpose( $parameter, \@phrase );
}
return @phrase, @$p;
}
sub int2name {
my @notes = @_;
# Convert integer pitch notation into MIDI note names
my %name;
my $int = -@notes;
for my $octave ( 3, 4, 5, 6 ) {
for my $note (@notes) {
$name{$int} = $note . $octave;
#warn "N:$int $name{$int} = $note . $octave\n";
$int++;
}
}
return %name;
}