-
Notifications
You must be signed in to change notification settings - Fork 5
/
primes-layered
103 lines (76 loc) · 2.4 KB
/
primes-layered
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
#!/usr/bin/env perl
###
#
# Play the prime numbers modulo the number of notes of the
# given scale in 2 parts: Fast (top) and Long (bottom)
#
# Examples:
# perl primes-layered # just use the defaults!
# perl primes-layered 2048 chromatic 300 0 73
# timidity primes-layered.mid
#
# * This tiny program uses global variables.
# Run away if that disturbs you.
#
###
use strict;
use warnings;
use Math::Prime::XS qw(primes);
use MIDI::Util qw(setup_score set_chan_patch);
use Music::Scales qw(get_scale_MIDI);
use constant BASE => 'C'; # scale note
use constant WHOLE => 'wn'; # bottom duration
use constant EIGHTH => 'en'; # top duration
use constant BEATS => 8; # number of eighths in a whole note
use constant BOCT => 3; # bottom octave
use constant TOCT => 4; # top octave
my $limit = shift || 1024; # upper bound of primes
my $scale = shift || 'major'; # scale name
my $bpm = shift || 120; # beats per minute
my $tpatch = shift // 4; # 4=electric piano
my $bpatch = shift // 95; # 95=synth
my $channel = 0; # the initial MIDI channel
my @primes = primes($limit);
my $score = setup_score(bpm => $bpm);
$score->synch(
\&top,
\&bottom,
);
$score->write_score("$0.mid");
sub top {
print "top()\n";
set_chan_patch($score, $channel++, $tpatch);
my @scale = (
get_scale_MIDI(BASE, TOCT, $scale),
get_scale_MIDI(BASE, TOCT + 1, $scale),
);
score_primes(\@scale, EIGHTH);
}
sub bottom {
print "bottom()\n";
set_chan_patch($score, $channel++, $bpatch);
my @scale = get_scale_MIDI(BASE, BOCT, $scale);
score_primes(\@scale, WHOLE);
}
sub score_primes {
my ($scale, $duration) = @_;
# select the appropriate number of primes to play
my @p = $duration eq WHOLE
? @primes[ 0 .. int @primes / BEATS ]
: @primes;
my $m = length scalar @$scale; # the modulo field width
my $n = length scalar @p; # the loop counter field width
my $w = length $p[-1]; # the prime field width
my $i = 0; # loop counter
for my $p (@p) {
$i++;
# get the position of the prime in the scale
my $mod = $p % @$scale;
# get the scale note, given the position
my $note = $scale->[$mod];
printf "\t%*d. P: %*d, Mod: %*d, Note: %d\n",
$n, $i, $w, $p, $m, $mod, $note;
# add the note to the score
$score->n($duration, $note);
}
}