Skip to content

Instantly share code, notes, and snippets.

@threedaymonk
Created November 8, 2021 00:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save threedaymonk/de5896d76dd456fef47f3ab91a13e8e5 to your computer and use it in GitHub Desktop.
Save threedaymonk/de5896d76dd456fef47f3ab91a13e8e5 to your computer and use it in GitHub Desktop.
disquiet0514
# 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
# 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