-
Notifications
You must be signed in to change notification settings - Fork 0
/
djsetlist.rb
94 lines (82 loc) · 2.48 KB
/
djsetlist.rb
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
# DO NOT USE THIS SCRIPT
# Use graph_setlist.rb instead
# This script does not implement early termination
# and will not terminate for any realistically sized
# playlist.
require 'set'
require 'yaml'
require 'pry'
$bpm_tolerance = 0.1
$camelot_wheel = {
"G#m": Set.new([:"C#m", :B, :Ebm]),
"Ebm": Set.new([:"G#m", :Gb, :Bbm]),
"Bbm": Set.new([:Ebm, :Db, :Fm]),
"Fm": Set.new([:Bbm, :Ab, :Cm]),
"Cm": Set.new([:Fm, :Eb, :Gm]),
"Gm": Set.new([:Cm, :Bb, :Dm]),
"Dm": Set.new([:Gm, :F, :Am]),
"Am": Set.new([:Dm, :C, :Em]),
"Em": Set.new([:Am, :G, :Bm]),
"Bm": Set.new([:Em, :D, :"F#m"]),
"F#m": Set.new([:Bm, :A, :"C#m"]),
"C#m": Set.new([:"F#m", :E, :"G#m"]),
"B": Set.new([:E, :"G#m", :Gb]),
"Gb": Set.new([:B, :Ebm, :Db]),
"Db": Set.new([:Gb, :Bbm, :Ab]),
"Ab": Set.new([:Db, :Fm, :Eb]),
"Eb": Set.new([:Ab, :Cm, :Bb]),
"Bb": Set.new([:Eb, :Gm, :F]),
"F": Set.new([:Bb, :Dm, :C]),
"C": Set.new([:F, :Am, :G]),
"G": Set.new([:C, :Em, :D]),
"D": Set.new([:G, :Bm, :A]),
"A": Set.new([:D, :"F#m", :E]),
"E": Set.new([:A, :"C#m", :B])
}
class DjSetlist
attr_accessor :songs
def initialize(file)
@songs = YAML.load(File.read(file))
end
def percentage_difference(a, b)
(a.to_f - b.to_f).abs / ((a + b) / 2.0)
end
def similar_bpm(s1, s2)
percentage_difference(s1[:bpm], s2[:bpm]) <= $bpm_tolerance
end
def same_key(s1, s2)
s1[:key].to_sym == s2[:key].to_sym
end
def adjacent_key(s1, s2)
$camelot_wheel[s1[:key].to_sym].include? s2[:key].to_sym
end
def compatible_key(s1, s2)
same_key(s1, s2) or adjacent_key(s1, s2)
end
def compatible_songs(song, candidate_songs)
candidate_songs.select {|s| compatible_key(song, s) and similar_bpm(song, s)}
end
def find_longest_chain_from(song, remaining_songs)
if remaining_songs.empty?
return [song]
end
possibly_next = compatible_songs(song, remaining_songs)
if possibly_next.empty?
return [song]
end
song_chains = possibly_next.map do |next_song|
[song] + find_longest_chain_from(next_song, remaining_songs.reject {|s| next_song == s})
end
song_chains.max_by(&:length)
end
def find_longest_chain
song_chains = @songs.map do |song|
find_longest_chain_from(song, @songs.reject {|s| song == s})
end
song_chains.max_by(&:length)
end
end
dj = DjSetlist.new('input/hull2021hype_full.yml')
songs = dj.songs
longest = dj.find_longest_chain()
File.open('output/hull2021hype_sorted.yml', 'w') {|f| f.write(longest.to_yaml) }