Skip to content

Instantly share code, notes, and snippets.

@omardelarosa
Last active June 13, 2018 02:31
Show Gist options
  • Save omardelarosa/e88a6ca7918aeae8d05c48602586bb59 to your computer and use it in GitHub Desktop.
Save omardelarosa/e88a6ca7918aeae8d05c48602586bb59 to your computer and use it in GitHub Desktop.
"Jellyfish #1" (or Markov Chain-based chill lofi hiphop for SonicPi)
###
### 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