Skip to content

Instantly share code, notes, and snippets.

@d0lfyn
Last active December 30, 2020 20:34
Show Gist options
  • Save d0lfyn/e28b3adaa8891f06e3a23e3a46be7745 to your computer and use it in GitHub Desktop.
Save d0lfyn/e28b3adaa8891f06e3a23e3a46be7745 to your computer and use it in GitHub Desktop.
Sonic Pi Polyphony
##| polyphony
##| v0.5
##| by d0lfyn (twitter: @0delphini)
##|
##| a simple generator for polyphony in 4 parts
##|
##| history:
##| v0.3
##| + add patterns
##| v0.4
##| + add turn-waiting
##| + add quits
##| + add pattern generation
##| + add patterns bank
##| + add pattern switching
##| v0.5
##| + add quantisation
##| + make patterns start and stop on tonic or dominant
##| + add pattern transposition behaviour
##| - remove is_above_or_equal predicate
##| + revise interval selection mechanism
use_bpm 600
use_synth :fm
use_random_seed 3
set :base_note, 0
set :note_1, 0
set :note_2, 0
set :note_3, 0
set :patterns, []
##| for manual patterns, specify notes in 0-indexed position on scale
set :pattern, []
set :pattern_length_min, 8
set :pattern_generation_factor, 20
set :pattern_repetition_factor, 4
set :pattern_switch_factor, 10
set :pattern_play_delay, 20
set :pattern_transposition_factor, 4
set :PATTERN_NOTE_VALUE, 0
set :PATTERN_NOTE_DURATION, 1
set :TONIC, 0
set :DOMINANT, 4
set :scale, scale(:c3, :minor)
set :quit_factor, 50
set :quit_time_min, 25
set :quit_time_max, 50
set :turn, 1
set :downbeat, 4
set :quantisation_factor, 5
set :quantisation_grid, 2
define :base do
generate_pattern
play_the_pattern :base_note, 0, get[:TONIC]
loop do
sync :time
if one_in(get[:pattern_repetition_factor])
play_the_pattern :base_note, 0, one_in(get[:pattern_transposition_factor]) ? rrand_i(-6,7) : get[:TONIC]
else
duration = choose_next_duration
play_now get[:base_note], 0, choose_next_amp, duration - 0.01
set :base_note, get[:base_note] + choose_next_interval
sleep duration - 0.01
end
if is_root(get[:base_note]) && one_in(get[:quit_factor])
break
end
end
end
define :contra do |register, time_state_note|
while(!(is_harmonious get[:base_note], get[time_state_note]))
sync :time
sleep 0.99
end
play_the_pattern time_state_note, register, [get[:TONIC], get[:DOMINANT]].choose
loop do
sync :time
if one_in(get[:pattern_repetition_factor])
play_the_pattern time_state_note, register, one_in(get[:pattern_transposition_factor]) ? rrand_i(-6,7) : get[:TONIC]
else
duration = choose_next_duration
play_now get[time_state_note], register, choose_next_amp, duration - 0.01
sleep duration - 0.01
##| interval = choose_next_interval
##| while(!(is_harmonious get[:base_note], get[time_state_note] + interval))
##| interval = choose_next_interval
##| end
##| set time_state_note, get[time_state_note] + interval
valid_interval = choose_valid_interval time_state_note
while valid_interval == nil
puts "no valid interval"
sync :time
sleep 0.99
valid_interval = choose_valid_interval time_state_note
end
set time_state_note, get[time_state_note] + valid_interval
end
if is_root(get[time_state_note]) && one_in(get[:quit_factor])
break
end
end
end
define :choose_next_amp do
return look % get[:downbeat] == 0 ? 0.2 : 0.1
end
define :choose_next_duration do
duration = 1
for i in 2..17
duration = i if one_in((i - 1) * 10)
end
disparity = (look + duration) % get[:quantisation_grid]
if disparity != 0 && one_in(get[:quantisation_factor])
duration += get[:quantisation_grid] - disparity
end
return duration
end
define :choose_next_interval do
interval = 1
for i in 2..7
interval = i if one_in(i * 2)
end
interval *= -1 if one_in(2)
return interval
end
define :choose_valid_interval do |time_state_note|
interval = nil
for i in 0..7
if is_harmonious get[:base_note], get[time_state_note] + i
interval = i if interval == nil || one_in(i + 1)
end
end
interval *= -1 if interval != nil && one_in(2)
return interval
end
define :generate_pattern do
pattern = []
next_note = [0, 4].choose
while (!(pattern.length >= get[:pattern_length_min] && (pattern[pattern.length - 1][get[:PATTERN_NOTE_VALUE]] % 8 == get[:TONIC] ||
pattern[pattern.length - 1][get[:PATTERN_NOTE_VALUE]] % 8 == get[:DOMINANT])))
pattern.push [next_note, choose_next_duration]
next_note += choose_next_interval
end
set :patterns, get[:patterns].take(get[:patterns].length).push(pattern)
set :pattern, pattern
end
##| define :is_above_or_equal do |note_1, note_2|
##| difference = note_2 % 8 - note_1 % 8
##| return difference >= 0
##| end
define :is_harmonious do |note_1, note_2|
difference = note_2 % 8 - note_1 % 8
return difference == 0 || difference == 2 || difference == 4 || difference == 5
end
define :is_root do |note_number|
return note_number % 8 == 0
end
define :play_beat do
sync :time
sample :drum_bass_soft, amp: 0.3, sustain: 0, release: 4
sleep 4
sample :drum_bass_soft, amp: 0.2, sustain: 0, release: 4
sleep 4
sample :drum_bass_soft, amp: 0.3, sustain: 0, release: 4
sleep 4
sample :drum_bass_soft, amp: 0.2, sustain: 0, release: 4
sleep 3.99
loop do
sync :time
sample :drum_bass_soft, amp: 0.3, sustain: 0, release: 4
sample :drum_cymbal_closed, amp: 0.1, sustain: 0, release: 2
sleep 2
sample :drum_cymbal_closed, amp: 0.05, sustain: 0, release: 2
sleep 2
sample :drum_bass_soft, amp: 0.2, sustain: 0, release: 4
sample :drum_snare_soft, amp: 0.3, sustain: 0, release: 4
sample :drum_cymbal_closed, amp: 0.1, sustain: 0, release: 2
sleep 2
sample :drum_cymbal_closed, amp: 0.05, sustain: 0, release: 2
sleep 1.99
end
end
define :play_now do |next_note, register, amplitude, duration|
if next_note != nil
play get[:scale][next_note] + register * 12, amp: amplitude, sustain: 0, release: duration
end
end
define :play_the_pattern do |time_state_note, register, transposition|
pattern = get[:pattern].take(get[:pattern].length)
if is_harmonious get[:base_note], pattern[0][get[:PATTERN_NOTE_VALUE]] + transposition
sync :time
for i in 0..(pattern.length - 1)
offset = i == pattern.length - 1 ? 0.01 : 0
play_now pattern[i][get[:PATTERN_NOTE_VALUE]] + transposition, register, choose_next_amp + 0.1,
pattern[i][get[:PATTERN_NOTE_DURATION]] - offset
set time_state_note, pattern[i][get[:PATTERN_NOTE_VALUE]]
sleep pattern[i][get[:PATTERN_NOTE_DURATION]] - offset
end
else
sync :time
sleep get[:quantisation_grid] - 0.01
end
end
define :quit do
sleep rrand_i(get[:quit_time_min], get[:quit_time_max])
end
define :switch_pattern do
set :pattern, get[:patterns].choose
end
define :wait_till_turn do |pTurn|
while (get[:turn] != pTurn)
sync :time
sleep 0.99
end
quit
set :turn, get[:turn] + 1
end
live_loop :keep_time do
cue :time
tick
sleep 1
end
in_thread(name: :pattern_play) do
loop do
if one_in(get[:pattern_generation_factor])
generate_pattern
end
if get[:patterns].length > 0 && one_in(get[:pattern_switch_factor])
switch_pattern
end
sleep get[:pattern_play_delay]
end
end
in_thread(name: :play_0) do
loop do
base
quit
end
end
in_thread(name: :play_1) do
wait_till_turn 1
loop do
contra 1, :note_1
quit
end
end
in_thread(name: :play_2) do
wait_till_turn 2
loop do
contra 2, :note_2
quit
end
end
in_thread(name: :play_3) do
wait_till_turn 3
loop do
contra 3, :note_3
quit
end
end
##| in_thread(name: :beat) do
##| play_beat
##| end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment