Configurable arpeggiator

Just made an arpeggiator method, which generates configurable arpeggios. It is also a nice example of a method with named parameters:

# p_tonic tonic of the chord
# p_name name of the chord
# p_length number of different pitches in the arpeggio
# p_invert inversion of the chord: 0, 1 or 2
# p_sleep number of beats for the arpeggio
# p_type: 0 ascending, 1 descending, 2 hill 3 valley
# p_random number of additional notes from 0 to 3 -> .shuffle gives a random selection
# p_has_basenote shall the base note be played (true or false)
def m_play_arpeggio(p_tonic: 60, p_name: :maj, p_length: 3, p_invert: 0, p_sleep: 2,
                    p_type: 0, p_random: 0, p_has_basenote: false, p_has_droplastnote: true)
  # damit sich die Zufallswerte nicht wiederholen
  use_random_seed Time.new.to_i
  
  # Chord definieren
  l_chord = (chord p_tonic, p_name, num_octaves: 4)
  
  # Inversion
  l_chord = l_chord.drop(p_invert)
  
  l_basenote = l_chord.take(1) - 12
  print l_basenote
  
  # Wir holen aus jedem beliebigen Chord diese Anzahl Noten + p_random
  l_chord = l_chord.take(p_length + p_random)
  print l_chord.notes
  
  # Jetzt mischeln wir diese, nehmen die ersten gemäss Anzahl und Sortieren
  l_chord = l_chord.shuffle.take(p_length).sort
  print l_chord.notes
  
  if p_type == 1 # absteigend
    l_chord = l_chord.reverse
  else
    if p_type == 2 # auf- dann absteigend
      # Mit .reflect hängen wir die Noten absteigend an
      l_chord = l_chord.reflect
    else
      if p_type == 3 # ab- dann aufsteigend
        l_chord = l_chord.reverse.reflect
      end
    end
  end
  
  # jetzt eventuell noch die letzte Note entfernen
  if p_has_droplastnote == true && p_type > 1
    l_chord = l_chord.drop_last(1)
  end
  
  print l_chord.notes
  p_length_arp = l_chord.length
  
  # spiele einen Grundton
  if p_has_basenote
    play l_basenote, sustain: p_sleep
  end
  
  l_chord.each do |p_note|
    play p_note
    sleep p_sleep * 1.0 / p_length_arp
  end
  
end


use_synth :tri

m_play_arpeggio(p_name: :min, p_length: 4, p_type: 3, p_has_basenote: false)
sleep 0.25
m_play_arpeggio(p_name: :min, p_length: 5, p_invert: 1, p_sleep: 1)
sleep 0.25
m_play_arpeggio(p_name: :min, p_length: 6, p_invert: 2, p_type: 1)
sleep 0.25
m_play_arpeggio(p_name: :min, p_length: 6, p_type: 2)
3 Likes

In the meantime I tweaked my arpeggiator, inserted another parameter and some lines of code. Now I can change between the internal Sonic Pi sound and external midi, for example an external software synthesizer like SurgeXT. I just recorded an mp3 file and published it on SoundCloud:
https://on.soundcloud.com/7omqx

This function is very versatile. I have had fun playing with it.
One thing I did ws to alter to to use standard Sonic Pi

define :name do.|parameter list|
...
end

#rather than Ruby's
def :name(paramter list)
...
end

I used the code below to cycle through all the defined chord names in Sonic Pi assigning varying parameters. Ended up with a rather mesmerising driving output. Other features spread the played arpeggios across the L-R spectrum. (Use phones to hear this better)

#perpetual meandering arpeggios based on Silvia Rothen's (@rothen_s) m_play+arpeggio code
#by Robin Newman Jan 2023

use_debug false
# p_tonic tonic of the chord
# p_name name of the chord
# p_length number of different pitches in the arpeggio
# p_invert inversion of the chord: 0, 1 or 2
# p_sleep number of beats for the arpeggio
# p_type: 0 ascending, 1 descending, 2 hill 3 valley
# p_random number of additional notes from 0 to 3 -> .shuffle gives a random selection
# p_has_basenote shall the base note be played (true or false)
# I changed the definition of m_play_arpeggio to standard Sonic Pi define.... do..end rather than def
define :m_play_arpeggio do |p_tonic: 60, p_name: :maj, p_length: 3, p_invert: 0, p_sleep: 2,
    p_type: 0, p_random: 0, p_has_basenote: false, p_has_droplastnote: true|
  # damit sich die Zufallswerte nicht wiederholen
  use_random_seed Time.new.to_i
  
  # Chord definieren
  l_chord = (chord p_tonic, p_name, num_octaves: 4)
  
  # Inversion
  l_chord = l_chord.drop(p_invert)
  
  l_basenote = l_chord.take(1) - 24 #bass note down two octaves
  print "bassnote",l_basenote.to_a
  
  # Wir holen aus jedem beliebigen Chord diese Anzahl Noten + p_random
  l_chord = l_chord.take(p_length + p_random)
  print "chordnotes",l_chord.notes
  
  # Jetzt mischeln wir diese, nehmen die ersten gemäss Anzahl und Sortieren
  l_chord = l_chord.shuffle.take(p_length).sort
  print "chord notes sorted",l_chord.notes
  
  if p_type == 1 # absteigend
    l_chord = l_chord.reverse
  else
    if p_type == 2 # auf- dann absteigend
      # Mit .reflect hängen wir die Noten absteigend an
      l_chord = l_chord.reflect
    else
      if p_type == 3 # ab- dann aufsteigend
        l_chord = l_chord.reverse.reflect
      end
    end
  end
  
  
  # jetzt eventuell noch die letzte Note entfernen
  if p_has_droplastnote == true && p_type > 1
    l_chord = l_chord.drop_last(1)
  end
  
  print "Playing",l_chord.notes
  p_length_arp = l_chord.length
  
  # spiele einen Grundton
  if p_has_basenote
    play l_basenote, sustain: p_sleep,amp: 1
  end
  
  l_chord.each do |p_note|
    play p_note,cutoff: 100,pan: rrand_i(-1,1)
    sleep p_sleep * 1.0 / p_length_arp
  end
end

with_fx :reverb, room: 0.7,mix: 0.7 do
  live_loop :arps do
    #change the synth each pass
    syn = [:tb303,:pulse,:dsaw].tick(:sn)
    use_synth syn
    chord_names.length.times do #utilise all defined chord names in Sonic Pi
      cn= chord_names.shuffle.tick #shuffle the order
      if tick%5==0 #every 5 arpeggios changes transpose root note
        use_transpose [0,5,7,-5].tick(:tr)
      end
      
      cl=chord(cn).length #number of notes in current chord
      
      density dice(2) do #repeat some twice, twice as fast
        puts cn,cl #print currentt chord aond length
        m_play_arpeggio(p_name: cn,p_length: cl, p_invert: 0 , p_sleep: 1,
                        p_type: (rrand_i(0,3)), p_random: (rrand_i(0,3)), p_has_basenote: true, p_has_droplastnote: true)
        sample :drum_tom_hi_hard,amp: 1
      end
    end
  end
end

Try altering the tempo slower or faster using the metronome.

Try also setting the p_has_basenote: parameter to false and commenting out the sample :drum_tom_hi_hard,amp: 1 line to just heard aprpeggio notes.

2 Likes

Hi Robin
Just tried out your code. I like it!

1 Like

Do you know if there is a list of all the defined chord names somewhere. I tried searching in the Sonic Pi tutorial documentations but didn’t see it there.

puts chord_names should do it

Hi Doffu
I found the chords in the Sonic Pi itself: There’s a file chord.rb, which can be opened with every text editor. In Windows I found it in the directory …\Sonic Pi\app\server\ruby\lib\sonicpi. In this file you find all chords and the intervals. If you need other chords, you can hack them in. I did this because I needed some jazz chords.
Greetings
Silvia

1 Like

Awesome! Thank you. Especially good to know that I can populate it with my own chords. I like working with chordiods rather than chords in a lot of cases, so this will come in handy!