Code used to generate the chord progression for A random walk from A to D, my piece for Disquiet Junto 0514
Created
November 8, 2021 00:18
-
-
Save threedaymonk/de5896d76dd456fef47f3ab91a13e8e5 to your computer and use it in GitHub Desktop.
disquiet0514
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Attempt to generate a sequence from Am9 to Dm9, only changing one note at each step, | |
# visiting all possible chords using the notes from both on the way. | |
start = [0, 0, 0, 1, 1, 1, 1] | |
def attempt(start) | |
prng = Random.new | |
current = start.dup | |
seq = [start.dup] | |
loop do | |
replacement = nil | |
1000.times do | |
a = prng.rand(current.length) | |
b = prng.rand(current.length - 1) | |
b += 1 if b >= a | |
replacement = current.dup | |
replacement[a] = current[b] | |
replacement[b] = current[a] | |
# Exit the loop if we found an unused chord to move to | |
break unless seq.include?(replacement) | |
end | |
# Give up if we didn't find a next step in 1000 tries | |
break if seq.include?(replacement) | |
seq << replacement.dup | |
current = replacement | |
end | |
return seq | |
end | |
loop do | |
seq = attempt(start) | |
# Test that we have a full-length sequence (7C4 = 35) and that it finishes with only one shared note. | |
# We'll worry about mapping places to notes later. | |
if seq.length == 35 && seq.last.drop(3).select { |a| a == 1 }.length == 1 | |
seq.each do |s| | |
puts s.join("") | |
end | |
exit | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# SonicPi script | |
use_bpm 80 | |
# start a2 c3 g3 b3 | |
# finish d2 f2 c3 e3 | |
# Map the bit positions to notes that satisfy the start and end constraints | |
keymap = %i[ d2 f2 e3 a2 g3 b3 c3 ] | |
# Opening and closing buildup/breakdown manually added | |
patterns = %w[ | |
0001000 | |
0001001 | |
0001101 | |
0001111 | |
1001011 | |
1001110 | |
0101110 | |
1101010 | |
0111010 | |
0011011 | |
0011101 | |
0010111 | |
1000111 | |
1010011 | |
1110010 | |
0110011 | |
0101011 | |
0101101 | |
0100111 | |
1100011 | |
1101001 | |
1100101 | |
1101100 | |
0111100 | |
1111000 | |
1011010 | |
1010110 | |
0011110 | |
0110110 | |
1100110 | |
1110100 | |
1010101 | |
1011100 | |
1001101 | |
1011001 | |
0111001 | |
0110101 | |
1110001 | |
1100001 | |
1100000 | |
1000000 | |
].map { |s| | |
s.scan(/./).zip(keymap).select { |a, b| a == "1" }.map(&:last) | |
} | |
# Compute the difference at each step, so we can send note on and off | |
last = [] | |
events = [] | |
patterns.each do |pat| | |
on = pat - last | |
off = last - pat | |
events << [on, off] | |
last = pat | |
end | |
# Turn all the lights off when we leave | |
events << [[], last] | |
live_loop :chords do | |
use_transpose 0 | |
idx = tick | |
on, off = events[idx] | |
# Turn off note to be removed, then wait for it to release before | |
# bringing in the next, so that we get a smooth transition | |
off.each { |n| midi_note_off n } if off | |
sleep 4 | |
on.each { |n| midi_note_on n, 100 } if on | |
sleep 4 | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment