-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathanalyze-recordings
136 lines (120 loc) · 3.72 KB
/
analyze-recordings
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/env perl
use strict;
use warnings;
# Write-up: https://ology.github.io/2022/01/21/led-zeppelin-in-seven-dimensions/
use GD::Chart::Radial;
use File::Slurper qw(read_lines write_binary);
use List::Util qw(max);
use Math::Utils qw(uniform_01scaling);
use Statistics::Basic qw(mean);
use WebService::AcousticBrainz;
use WebService::MusicBrainz;
use constant PATH => $ENV{HOME} . '/tmp/radial/'; # Where the charts will be created
use constant SIZE => 700; # Chart pixel dimensions
my $mbid_file = shift || ''; # Optional. Use __DATA__ if not given
die "Chart directory does not exist\n" unless -d PATH;
# Sonic measurements to chart
my @stats = qw(
dissonance
dynamic_complexity
spectral_complexity
spectral_energy
spectral_entropy
danceability
chords_changes_rate
);
# Fetch the interesting mbids
my @releases;
if ($mbid_file && -e $mbid_file) {
@releases = read_lines($mbid_file);
}
else {
while (my $id = readline(DATA)) {
chomp $id;
push @releases, $id;
}
}
my $i = 0;
my $mb = WebService::MusicBrainz->new;
my @mbids;
for my $release (@releases) {
$i++;
warn "$i. Release: $release\n";
my $result = $mb->search(release => { mbid => $release, inc => ['recordings'] });
my @ids = map { $_->{recording}{id} } $result->{media}[0]{tracks}->@*;
push @mbids, @ids;
sleep 4; # play nice
}
die "No MBIDs to process.\n" unless @mbids;
# Gather sonic measurements
$i = 0;
my $ab = WebService::AcousticBrainz->new;
my %titles;
for my $mbid (@mbids) {
$i++;
warn "$i. Recording: $mbid\n";
my $r = $ab->fetch(mbid => $mbid, endpoint => 'low-level');
my $title = $r->{metadata}{tags}{title}[0];
$titles{$title} = {
dissonance => $r->{lowlevel}{dissonance}{mean},
dynamic_complexity => $r->{lowlevel}{dynamic_complexity},
spectral_complexity => $r->{lowlevel}{spectral_complexity}{mean},
spectral_energy => $r->{lowlevel}{spectral_energy}{mean},
spectral_entropy => $r->{lowlevel}{spectral_entropy}{mean},
danceability => $r->{rhythm}{danceability},
chords_changes_rate => $r->{tonal}{chords_changes_rate},
};
sleep 4; # play nice
}
# Scale the measurements to [0,1]
my %maxs;
for my $stat (@stats) {
$maxs{$stat} = max(map { $titles{$_}->{$stat} } keys %titles);
}
for my $title (keys %titles) {
for my $stat (@stats) {
($titles{$title}->{$stat}) = uniform_01scaling([0, $maxs{$stat}], $titles{$title}->{$stat});
}
}
# Compute the measurement averages
my %avgs;
for my $title (keys %titles) {
for my $stat (@stats) {
push $avgs{$stat}->@*, $titles{$title}->{$stat};
}
}
%avgs = map { $_ => mean($avgs{$_}) } keys %avgs;
my $key = (keys %titles)[0];
my $avgs = [ map { $avgs{$_} } sort keys %{ $titles{$key} } ];
# Get the measurement names
my $names = [ sort @stats ];
# Create the charts
for my $title (sort keys %titles) {
my $chart = GD::Chart::Radial->new(SIZE, SIZE);
$chart->set(
title => $title,
y_max_value => 1,
colours => [qw(white black grey red)],
);
my @data = (
$names,
$avgs,
[ map { $titles{$title}->{$_} } sort keys %{ $titles{$title} } ],
);
$chart->plot(\@data);
$title =~ s/\W/_/g;
my $file = PATH . $title . '.png';
warn "Creating: $file\n";
write_binary($file, $chart->png);
}
# Led Zeppelin US releases:
__DATA__
3df3b60f-d6e1-3af9-913f-0014e73650ee
d7be0adc-dbcb-3a6c-878c-ddac4dc711f5
7aadcfa2-df82-480e-8d2d-7ec4d0b41172
71eafe5d-33b0-4e41-9b51-754b8450302e
3ccb4cb2-940a-4e2e-b1fd-4c0b7483280f
0d06025c-afff-49fd-a1db-8005e686e4d9
0c6e631d-4d64-3313-80d5-505eccbfbffa
d1184ff9-8667-4141-b7f5-b021202d793c
7e2e0d82-9b09-35c2-8e1f-357cb5bb419a