Last active
June 13, 2018 02:31
-
-
Save omardelarosa/e88a6ca7918aeae8d05c48602586bb59 to your computer and use it in GitHub Desktop.
"Jellyfish #1" (or Markov Chain-based chill lofi hiphop for SonicPi)
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
### | |
### This is a generative/randomized piece of music "composed" by Omar Delarosa | |
### | |
### To see it in context, run this file in SonicPi. | |
### | |
### Or visit on YouTube at: https://youtu.be/MBrVGzMIpRM | |
### | |
use_bpm 70 | |
T = 4.0 | |
# State machine utility functions | |
define :markov do |a, h| h[a].sample; end # Chooses the next state at random from hash | |
define :g do |k| get[k]; end # simplified root note in scale getter | |
define :s do |k, n| set k, n; end # simplified root note setter | |
define :mnote do |key,chain| s key, (markov (g key), chain); g key; end | |
# Pattern parsing | |
define :ringify do |p| p.gsub(' ', '') .split('').map {|n| n.to_i }.ring; end | |
# Init states for markov chains | |
set :k, 1 | |
set :b, 0 | |
set :s, 0 | |
# Plays a note - this is a function in order to seamlessly use MIDI instead | |
define :pplay do |n, d = 0.1, m = 0.0, r = 0.0, syn = :fm| | |
with_fx :reverb, mix: r do | |
with_fx :wobble, mix: m do | |
use_synth syn # Using FM for portability | |
play n, release: d, sustain: 0.1 # This can also be MIDI | |
end | |
end | |
end | |
# Scale | |
sc_root = :D2 | |
sc_type = :major | |
sc = scale(sc_root, sc_type) | |
# Chords in scale | |
chords = (1..7).map {|n| chord_degree n, sc_root, sc_type }.ring | |
# Elaboration/Arrangement | |
elaboration_complexities = (knit, 2, 4, 3, 2, 4, 2) | |
# Levels hash | |
L = { | |
ppiano: 1.0, | |
kick: 1.0, | |
snare: 1.0, | |
hats: 1.0 | |
} | |
# Tone families | |
TONES = [:pulse, :fm] | |
# Rhythm patterns | |
p1 = [ | |
'4000 0000 0040 0000', | |
'4020 0000 4000 0000', | |
'4023 0010 4020 0000', | |
'4030 0020 4020 1000' | |
] | |
# Kick drum markov chain | |
B = { | |
0 => [0, 1, 0, 2], | |
1 => [0], | |
2 => [1, 0, 3], | |
3 => [0] | |
} | |
p2 = [ | |
'0000 4000', | |
'0000 4100', | |
'1000 4000', | |
'0200 4100', | |
'1001 3001' | |
] | |
# Snare drum markov chain | |
S = { | |
0 => [0, 0, 0, 0, 1, 2, 3], | |
1 => [0, 0, 1], | |
2 => [1, 0, 4, 3], | |
3 => [0], | |
4 => [0] | |
} | |
# Markov chain | |
K = { | |
1 => [7], | |
2 => [1], | |
7 => [2, 5], | |
5 => [1, 6], | |
6 => [2], | |
2 => [5] | |
} | |
define :pchord do |chr, d = T| | |
with_fx :level, amp: 0.3 do | |
pplay chr[0], d, 0.8 | |
pplay chr[1] + 24, d, 0.4 | |
pplay chr[2], d, 0.8 | |
pplay chr[3] + 12, d, 0.4 | |
end | |
end | |
# Main loop | |
live_loop :ppiano do | |
chr = chords[mnote :k, K] # get next note state from markov chain | |
pchord chr # Chord (left hand) | |
complexity = elaboration_complexities.tick | |
sleep T/(2**complexity) | |
l = L[:ppiano] || 0.0 | |
t = :pulse | |
t = TONES.sample if one_in(4) # randomly select tone | |
(3*(complexity-1)-1).times do | |
with_fx :level, amp: l do | |
pplay chr[rrand(-3,3).to_i] + 24, (T/complexity-1), nil, nil, :pulse # Melody (right hand) | |
sleep T/(2**complexity) | |
end | |
end | |
chr2 = chords[mnote :k, K] # get next note state from markov chain | |
pchord chr2, T/4 # Chord (left hand) | |
(1*(complexity-1)).times do | |
with_fx :level, amp: l do | |
pplay chr2[rrand(-3,3).to_i] + 24, (T/complexity-1), nil, nil, :pulse # Melody (right hand) | |
sleep T/(2**complexity) | |
end | |
end | |
end | |
live_loop :kick do | |
p = ringify(p1[mnote :b, B]) # retrieve and ringify pattern | |
l = L[:kick] || 0.0 | |
density (p.length / 2) do | |
with_fx :level, amp: l do | |
sample :bd_fat, amp: 3.5 if p.tick >= 1 | |
sleep 1 | |
end | |
end | |
end | |
# Not a markov chain, the only fully looped, static element. :( | |
p3 = (knit, T/16, 4, T/24, 6, T/16, 4, T/24, 6, T/16, 4, T/64, 8) # hihats | |
live_loop :hats do | |
l = L[:hats] || 0.0 | |
16.times do | |
with_fx :level, amp: l do | |
sample :elec_tick, amp: 1.5, decay: 0.1 | |
sleep p3.tick | |
end | |
end | |
end | |
live_loop :snare do | |
p = ringify(p2[mnote :s, S]) # retrieve and ringify pattern | |
l = L[:snare] || 0.0 | |
density (p.length / 2) do | |
with_fx :level, amp: l do | |
sample :sn_dolf if p.tick >= 1 | |
sleep 1 | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment