forked from ology/Music
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cryptomorphic
117 lines (86 loc) · 2.75 KB
/
cryptomorphic
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
#!/usr/bin/env perl
use strict;
use warnings;
# Prefer my local libraries
use lib map { "$ENV{HOME}/sandbox/$_/lib" } qw(MIDI-Drummer-Tiny MIDI-Util Music-Duration-Partition);
use List::Util qw/ shuffle /;
use MIDI::Drummer::Tiny; # https://metacpan.org/pod/MIDI::Drummer::Tiny
use MIDI::Util qw(setup_score set_chan_patch); # https://metacpan.org/pod/MIDI::Util
use Music::Duration::Partition; # https://metacpan.org/pod/Music::Duration::Partition
use Music::Scales;
use Music::VoiceGen;
use Music::Voss qw/ powers /;
my $size = shift || 8;
my $max = shift || 8;
my $bpm = shift || 90;
my $mod = shift || 2; # Default: alternate
my $top_patch = 0;
my $bottom_patch = 42;
my $score = setup_score( bpm => $bpm );
$score->synch(
\&melody,
\&bass,
\&beat,
);
$score->write_score("$0.mid");
sub beat {
my $d = MIDI::Drummer::Tiny->new( bpm => $bpm, score => $score );
for my $i ( 1 .. $max * $size + ( $mod * $max ) ) {
$d->note( $d->quarter, $d->closed_hh );
}
}
sub melody {
set_chan_patch( $score, 0, $top_patch );
my $mdp = Music::Duration::Partition->new(
size => $size,
# pool => [qw/ twn thn tqn ten tsn /],
pool => [qw/ qn en sn /],
);
my $motif = $mdp->motif;
my ( $scale, $genf ) = get_genf( 'A', 5, 'minor' );
for my $i ( 1 .. $max ) {
my @notes = map { $scale->[ $genf->($_) % @$scale ] } 0 .. @$motif - 1;
my @phrase = $i % $mod ? @$motif : shuffle @$motif;
for my $n ( 0 .. @notes - 1 ) {
$score->n( $phrase[$n], $notes[$n] );
# $score->n( $motif->[$n], $notes[$n] );
}
# $score->r('wn');
}
}
sub bass {
set_chan_patch( $score, 1, $bottom_patch );
my $mdp = Music::Duration::Partition->new(
size => $size,
pool => [qw/ wn hn /],
);
my $motif = $mdp->motif;
my @intervals = qw/ -4 -3 -2 2 3 4 /;
my @pitches = get_scale_MIDI( 'A', 1, 'pminor' );
my $voice = Music::VoiceGen->new(
pitches => \@pitches,
intervals => \@intervals,
);
for my $i ( 1 .. $max ) {
my @notes = map { $voice->rand } 0 .. @$motif - 1;
my @phrase = $i % 2 ? @$motif : shuffle @$motif;
for my $n ( 0 .. @notes - 1 ) {
$score->n( $phrase[$n], $notes[$n] );
# $score->n( $motif->[$n], $notes[$n] );
}
$score->n( 'wn', $voice->rand );
}
$score->n( 'wn', $pitches[0] );
}
sub get_genf {
my ( $note, $octave, $type ) = @_;
my @scale = map { $_ . $octave } get_scale_notes( $note, $type );
# Transform to MIDI accidentals
for ( @scale ) {
s/#/s/;
s/b/f/;
}
my $seed = [ map { sub { int rand 2 } } @scale ];
my $genf = powers( calls => $seed );
return \@scale, $genf;
}