Last active
December 30, 2020 20:34
-
-
Save d0lfyn/e28b3adaa8891f06e3a23e3a46be7745 to your computer and use it in GitHub Desktop.
Sonic Pi Polyphony
This file contains hidden or 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
##| 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