Coding in the style of Arvo Pärt and... some questions!

You can also use lambdas like blocks:

#code arvo pärt
#fonctions tintinnabuli inspirée de fur alina
#version polyphonique play

drone = lambda do
  play :B2, release: 5, amp: rrand(0.50, 1)
  play :B0, release: 5, amp: rrand(0.50, 1)
  sleep choose([2, 3, 4, 5])
end

a = lambda do
  play :B3, release: 5, amp: rrand(0.50, 1)
  sleep choose([0, 1])
  play :Fs2, release: 5, amp: rrand(0.50, 1)
  sleep choose([0.5, 1, 2, 3, 4, 5])
end

b = lambda do
  play choose([:Cs4,:D4]), attack: choose([0.2,0.5]), release: 3, amp: rrand(0.50, 1)
  sleep choose([0, 1])
  play :B2, attack: choose([0.2,0.5]), release: 4, amp: rrand(0.50, 1)
  sleep choose([0.5, 1, 2, 3, 4, 5])
end

c = lambda do
  play choose([:E4,:Fs4]), attack: choose([0.2,0.5]), release: 3, amp: rrand(0.50, 1)
  sleep choose([0, 1])
  play :D3, attack: choose([0.2,0.5]), release: 4, amp: rrand(0.50, 1)
  sleep choose([0.5, 1, 2, 3, 4, 5])
end

d = lambda do
  play choose([:G4,:A4,:B4]), attack: choose([0.2,0.5]), release: 3, amp: rrand(0.50, 1)
  sleep choose([0, 1])
  play :Fs3, attack: choose([0.2,0.5]), release: 4, amp: rrand(0.50, 1)
  sleep choose([0.5, 1, 2, 3, 4, 5])
end

e = lambda do
  play choose([:Cs4,:D4]), attack: choose([0.2,0.5]), release: 3, amp: rrand(0.50, 1)
  sleep choose([0, 1])
  play :B3, attack: choose([0.2,0.5]), release: 4, amp: rrand(0.50, 1)
  sleep choose([0.5, 1, 2, 3, 4, 5])
end

f = lambda do
  play choose([:E5,:Fs5]), attack: choose([0.2,0.5]), release: 3, amp: rrand(0.50, 1)
  sleep choose([0, 1])
  play :D4, attack: choose([0.2,0.5]), release: 4, amp: rrand(0.50, 1)
  sleep choose([0.5, 1, 2, 3, 4, 5])
end

g = lambda do
  play choose([:G5,:A5,:B5]), attack: choose([0.2,0.5]), release: 3, amp: rrand(0.50, 1)
  sleep choose([0, 1])
  play :Fs4, attack: choose([0.2,0.5]), release: 4, amp: rrand(0.50, 1)
  sleep choose([0.5, 1, 2, 3, 4, 5])
end

h = lambda do
  play choose([:Cs6,:D6]), attack: choose([0.2,0.5]), release: 3, amp: rrand(0.50, 1)
  sleep choose([0, 1])
  play :B4, attack: choose([0.2,0.5]), release: 4, amp: rrand(0.50, 1)
  sleep choose([0.5, 1, 2, 3, 4, 5])
end

flower = lambda do
  play :Fs5, attack: choose([0.2,0.5]), release: 3, amp: rrand(0.50, 1)
  sleep choose([0, 1])
  play :Cs4, attack: choose([0.2,0.5]), release: 4, amp: rrand(0.50, 1)
  sleep choose([0.5, 1, 2, 3, 4, 5])
end

bfa = lambda do
  play :D4, attack: choose([0.2,0.5]), release: 3, amp: rrand(0.50, 1)
  sleep choose([0, 1])
  play :B2, attack: choose([0.2,0.5]), release: 4, amp: rrand(0.50, 1)
  sleep choose([0.5, 1, 2, 3, 4, 5])
end

dfa = lambda do
  play :B4, attack: choose([0.2,0.5]), release: 3, amp: rrand(0.50, 1)
  sleep choose([0, 1])
  play :Fs3, attack: choose([0.2,0.5]), release: 4, amp: rrand(0.50, 1)
  sleep choose([0.5, 1, 2, 3, 4, 5])
end

#structure

in_thread(name: :arvopart) do
  #uncomment to use one or another
  #use_play_defaults port: ‘focusrite_usb_play’
  #use_play_defaults port: ‘arturia_minibrute_2’
  
  with_fx :reverb, room: 0.9 do
    
    loop do
      
      use_synth :hollow
      drone.call
      
      14.times do
        [a, b, c, d, e, f, g, h, flower].choose.call
      end
      
    end
  end
end

… and great post! Thanks! I also do love Arvo Pärts music.

2 Likes

Thanks a lot @amiika ! Pärt’s music is perfect I think for this type of project. His system is really logical, simple (in a sense) and can be adapted to a lot of musical situations. And it’s nice to the ear !

Thanks @pashultz , it’s really strictly the notes from Fur Alina, except for d and b functions, which must be replaced by bfa and dfa functions.
Actually, just for fun, the right “structure” of the piece is this one :

  #fur alina structure
  #[e, e]
  #[f,f,f]
  #[f,e,e,d]
  #[f,f,f,e,f]
  #[e,f,g,h,f,e]
  #[f,e,d,e,f,e,f]
  #[f,h,h,f,d,f,g,g]
  #[e,f,d,e,e,e,d]
  #[g,g,g,d,e,f]
  #[g,e,f,f,flower]
  #[e,d,b,c]
  #[a,c,e]
  #[d,b]
  #[d,e,d]

I named the “flower” function after the particular status that these notes have in the piece. It’s the only “exception” to the strict tintinnabuli system, and I think I remember that in the “24 preludes” documentary about Pärt he talks about a flower he drew on the sheet next to these notes.

I did a “paper/pencil” analysis of the piece and thought about how to make a sonic pi traduction. I can post a photo of the analysis but my writting is terrible ^^ .

Oh, and if you read french, I wrote a book about Arvo Pärt 8 years ago ! It’s about how his music share some common ideas with the writings of Philip K. Dick.

1 Like

Also, I want to share this with you. When I was writing my book I discovered this article, and it really is the first time I heard of someone attempting to do something similar.

1 Like

Great stuff … wish I could understand that Mathematica code. Can someone write ruby version for that? :slight_smile:

Here’s my try:

define :prime_differences do |s|
  result= [1, 2]
  last_prime = 5
  (7..s).map do |i|
    if s%i != 0
      if ((2..Math.sqrt(i)).none? { |n| (i % n).zero? })
        result.push(i - last_prime)
        last_prime = i
      end
    end
  end
  result
end


idx = prime_differences 1000
print idx

sca = scale :A2, :harmonic_minor, num_octaves: 4

notes = []
idx.map do |i|
  notes.push(sca[i-1])
end

print notes

(maybe some lines of the code seem familiar to you … :sunglasses:)

2 Likes

Ah, yes. That’s the simple part, now how about the second part of the process :slight_smile:

I made that into a Enumerators (Would put not included in Sonic pi tag here if I knew how) - but i just love those enumerators. Also added norgards infinite sequence as a bonus, would be great to try the tintinnabuliation on that one:

define :prime_difference do
  Enumerator.new do |y|
    prime = 2
    next_prime = 3
    primes = (5..).lazy.select{|i| (2..Math.sqrt(i)).none? {|n| (i % n).zero?}}
    while true
      y << next_prime-prime
      prime = next_prime
      next_prime = primes.next
    end
  end
end

# https://web.archive.org/web/20071010092358/http://www.pernoergaard.dk/eng/strukturer/uendelig/ukonstruktion03.html
define :norgard do
  Enumerator.new do |y|
    n = 1
    while true
      y << n.to_s(2).chars.reduce(0) {|s,b| b == '1' ? s+1 : -s }
      n+=1
    end
  end
end

print prime_difference.first(100)
print norgard.first(100)

In fact I like enumerators so much that I have enumerators inside my enumerators :wink:

1 Like

Not sure whether I understand the tintinnabuli algorithm correctly. But this is how I interpret it on basis of the link given above:

use_debug false
st = 1.0

define :prime_difference do
  Enumerator.new do |y|
    prime = 2
    next_prime = 3
    primes = (5..).lazy.select{|i| (2..Math.sqrt(i)).none? {|n| (i % n).zero?}}
    while true
      y << next_prime-prime
      prime = next_prime
      next_prime = primes.next
    end
  end
end

# https://web.archive.org/web/20071010092358/http://www.pernoergaard.dk/eng/strukturer/uendelig/ukonstruktion03.html
define :norgard do
  Enumerator.new do |y|
    n = 1
    while true
      y << n.to_s(2).chars.reduce(0) {|s,b| b == '1' ? s+1 : -s }
      n+=1
    end
  end
end

#print prime_difference.first(100)
#print norgard.first(100)

sca = scale :A2, :minor, num_octaves: 5
triads = []  # = notes of chords in all octaves
5.times do |i|
  triads.push sca[i*7]
  triads.push sca[i*7 + 2]
  triads.push sca[i*7 + 4]
end

define :get_nearest_triad do |n, i, o|
  # n: note
  # i: distance of triad note: [-3, -2, -1, 1, 2, 3]
  # o: num octaves above n
  idx = 4
  triads.each_with_index do |t, ii|
    next if t < n + 12*o
    idx = (ii + i)%triads.length
    break
  end
  triads[idx]
end

define :gen_m_voice do
  Enumerator.new do |n|
    pd = prime_difference
    while true
      idx = pd.next
      n << sca[idx - 1]
    end
  end
end

# M-voice
mv = gen_m_voice
set :note, sca[0]

with_fx :reverb, room: 0.6, mix: 0.3 do
  with_fx :flanger, feedback: 0.3 do
    with_fx :lpf, mix: 0.5, cutoff: 70 do
      live_loop :m_voice, auto_cue: false  do
        n = mv.next
        set :note, n
        synth :tri, note: n, attack: 0.01, sustain: st, release: 0.1, amp: 0.6
        sleep st
      end
    end
  end
end

# T-voices
with_fx :reverb, room: 0.8 do
  live_loop :t_voice_1 do
    sync :note
    n = get :note
    d = 1
    density d do |dd|
      t = get_nearest_triad n, 1, 1
      play t, release: 0.8*st if one_in 4
      sleep st if dd < d - 1
    end
  end
end

with_fx :reverb, room: 0.8 do
  live_loop :t_voice_2 do
    sync :note
    n = get :note
    d = 2
    density d do |dd|
      t = get_nearest_triad n, 2, 1
      play t, release: 0.8*st if one_in 2
      sleep st if dd < d - 1
    end
  end
end

with_fx :reverb, room: 0.8 do
  live_loop :t_voice_3 do
    sync :note
    n = get :note
    d = 1
    density d do |dd|
      t = get_nearest_triad n, -2, 2
      play t, release: 0.8*st if one_in 2
      sleep st if dd < d - 1
    end
  end
end

with_fx :reverb, room: 0.8 do
  live_loop :t_voice_4 do
    sync :note
    n = get :note
    d = 2
    density d do |dd|
      t = get_nearest_triad n, -1, 3
      play t, release: 0.8*st if one_in 3
      sleep st if dd < d - 1
    end
  end
end
2 Likes

Sounds pretty solid to me. I have to take timeout and study more. Watched this wonderful lecture about Arvo Pärts sacred minimalism and there are potentially many really simple yet wonderful compositional ideas that could be used with Sonic Pi.

1 Like

Thanks for the link to this very interesting lecture. I learned that Pärt uses the T-voices mostly with the same rhytm as the M-voice, but also alternating the nearest-triads-index. Therefore, I incorporated that into the code and created an example with this setting and the norgard sequence as the M-voice.

use_debug false
st = 1.5

define :prime_difference do
  Enumerator.new do |y|
    prime = 2
    next_prime = 3
    primes = (5..).lazy.select{|i| (2..Math.sqrt(i)).none? {|n| (i % n).zero?}}
    while true
      y << next_prime-prime
      prime = next_prime
      next_prime = primes.next
    end
  end
end

# https://web.archive.org/web/20071010092358/http://www.pernoergaard.dk/eng/strukturer/uendelig/ukonstruktion03.html
define :norgard do
  Enumerator.new do |y|
    n = 1
    while true
      y << n.to_s(2).chars.reduce(0) {|s,b| b == '1' ? s+1 : -s }
      n+=1
    end
  end
end

#print prime_difference.first(100)
#print norgard.first(100)

sca = scale :A2, :minor, num_octaves: 5
triads = []  # = notes of chords in all octaves
5.times do |i|
  triads.push sca[i*7]
  triads.push sca[i*7 + 2]
  triads.push sca[i*7 + 4]
end

define :get_nearest_triad do |n, i, o|
  # n: note
  # i: distance of triad note: [-3, -2, -1, 1, 2, 3]
  # o: num octaves above n
  idx = 4
  tn = n + 12*o  # transposed n
  triads.each_with_index do |t, ii|
    next if t < tn
    idx = (ii + i)%triads.length
    break
  end
  triads[idx]
end

define :gen_m_voice do
  Enumerator.new do |n|
    #gen = prime_difference
    gen = norgard
    while true
      idx = gen.next
      #n << sca[idx - 1]
      n << sca[12 + idx]
    end
  end
end

# M-voice
mv = gen_m_voice
set :note, sca[0]

with_fx :reverb, room: 0.6, mix: 0.3 do
  with_fx :flanger, feedback: 0.3 do
    with_fx :lpf, mix: 0.5, cutoff: 70 do
      live_loop :m_voice, auto_cue: false, delay: 0.2  do
        d = [2, 2, 2, 2, 2, 1].choose
        set :density, d
        density d do |dd|
          n = mv.next
          set :note, n
          synth :tri, note: n, attack: 0.01, sustain: st-0.2, release: 0.1, amp: 0.6
          sleep st
        end
      end
    end
  end
end


# T-voices
with_fx :reverb, room: 0.8 do
  live_loop :t_voice_1 do
    sync :note
    n = get :note
    d = get :density
    density d do |dd|
      t = get_nearest_triad n, [-1, 1].choose, 0
      play t, release: 0.8*st
      sleep st if dd < d - 1
    end
  end
end

with_fx :reverb, room: 0.8 do
  live_loop :t_voice_2 do
    sync :note
    n = get :note
    d = get :density
    density d do |dd|
      t = get_nearest_triad n, [-1, 1].choose, 1
      play t, release: 0.8*st, amp: 0.5
      sleep st if dd < d - 1
    end
  end
end
1 Like