forked from ology/Music
-
Notifications
You must be signed in to change notification settings - Fork 0
/
stat-walk-sync
89 lines (64 loc) · 2.07 KB
/
stat-walk-sync
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
#!/usr/bin/env perl
use strict;
use warnings;
# To run this, first produce a freq file with note-transition-sync.
use Data::Dumper::Compact qw(ddc);
use lib map { "$ENV{HOME}/sandbox/$_/lib" } qw(Graph-Weighted MIDI-Util);
use Graph::Weighted;
use List::Util::WeightedChoice qw( choose_weighted );
use MIDI::Util qw(setup_score set_chan_patch);
use Storable;
my $file = shift || die "Usage: perl $0 proportional-frequencies.dat [0|1]";
my $n = shift || 4; # Number of notes to play
my $initial = shift // 1; # Initial graph node: 0=random, 1=lowest note
my $patch = shift // 42;
my $channel = 0;
my @phrases;
my $data = retrieve($file);
#warn 'Data to process: ', ddc($data);
my $score = setup_score( patch => $patch );
for my $track ( keys %$data ) {
if ( $initial == 0 ) {
$initial = ( keys %{ $data->{$track} } )[ int( rand keys %{ $data->{$track} } ) ];
}
else {
$initial = ( sort { $a <=> $b } keys %{ $data->{$track} } )[0];
}
#warn "Initial note: $initial\n";
my $pitches = Graph::Weighted->new;
$pitches->populate( $data->{$track} );
my @phrase = collect_notes( $n, $initial, $pitches );
#warn ddc(\@phrase);
my $func = sub {
set_chan_patch( $score, $channel++, $patch );
$score->n( @$_ ) for @phrase;
};
push @phrases, $func;
}
$score->synch(@phrases);
$score->n( 'wn', $initial );
$score->write_score( "$0.mid" );
sub collect_notes {
my ( $n, $initial, $graph ) = @_;
my $p_vertex = $initial;
my $notes = [];
for my $i ( 1 .. $n ) {
push @$notes, [ 'qn', 'f', $p_vertex ];
if ( $i < $n ) {
$p_vertex = next_vertex( $graph, $p_vertex );
}
}
return @$notes;
}
sub next_vertex {
my ( $graph, $vertex ) = @_;
my $successors = [];
for my $successor ( $graph->successors($vertex) ) {
push @$successors, {
vertex => $successor,
weight => $graph->get_cost( [ $vertex, $successor ] ),
};
}
my $choice = choose_weighted( $successors, sub { $_[0]->{weight} } );
return $choice->{vertex};
}