Sonic Pi Live Coding with Algorithmic Music Generator

Hi,

I have been working on a project, that integrates Sonic Pi with an algorithmic music generator, mentioned in this thread.

This project was inspired by ​ Live Coding with Machine Learning (Magenta.js).

Here is a demo of the first iteration:

The application supports integration with Sonic Pi, utilizing the Open Sound Control protocol. Melody variations can be generated from MIDI sources and from messages sent by Sonic Pi directly in real-time. Two backends for algorithmic music generation are implemented: Markov chains and a wrapper around Magenta’s MusicRNN chord_pitches_improv checkpoint.

The source code needs to be polished a bit, but I will upload it on Github soon.

Would really appreciate any feedback and comments :slight_smile:

4 Likes

Very interesting. I look forward to the completed project.

Nicely done!

It’s sounding really awesome so far! Personally, I’d run the piano through a subtle morphing high-cut effect to give the piano some more movement but that’s just me.

@Ajoe Thanks! :slight_smile:

@Doffu Thanks! Here is the code for the piano part and the loops that receive and play the generated sequences. Would really appreciate any suggestions on how to optimize it or make the sound better :slight_smile:

# Coded by Viktor Pavlov
# Piano part from Arvo Pärt's "Spiegel im Spiegel"

use_bpm 80

set :sequence, []

live_loop :receive_sequence do
  use_real_time
  seq = sync "/osc*/gen/sequence"
  s = sync "/osc*/gen/steps"
  set :sequence, seq.zip(s)
end

live_loop :metronome do
  sleep 1
end

with_fx :reverb, room: 1 do
  live_loop :piano_part, sync: :metronome do
    F = (ring :C5, :F5, :A5)
    Gm7 = (ring :As4, :F5, :G5)
    Bbmaj7 = (ring :D5, :A5, :Bb5)
    
    use_synth :piano
    use_synth_defaults sustain: 0.8, release: 0.2, hard: 0.1
    18.times do
      play F.tick
      sleep 1
    end
    6.times do
      play Gm7.tick
      sleep 1
    end
    24.times do
      play F.tick
      sleep 1
    end
    6.times do
      play Bbmaj7.tick
      sleep 1
    end
  end
  
  live_loop :play_gen_sequence, sync: :metronome do
    notes = get[:sequence] || []
    puts notes
    
    if notes.empty?
      sleep 1
    else
      notes.each do |note, step|
        use_synth :blade
        use_synth_defaults amp: 0.4, attack: step * 0.4, decay: step * 0.1,
          sustain: step * 0.3, release: step * 0.2, vibrato_rate: 7
        play note
        use_synth :square
        play note, amp: 0.03
        sleep step
      end
    end
  end
end

hi there

Interesting as coding is concerned but musically “it lacks sweat” and human feelings. We are better than machines to express our feelings with music.
Just my point of view.
Next step : how to add sweat into a sonic pi tune :slight_smile:
see you

1 Like

Something like this… keep in mind this isn’t perfect. I’m not 100% grasping the way that you coded this piece as it’s very different from how I typically code. There is probably a much better way to code this automation in your own personal way but this should give you an idea of how you could automate the LPF effect.

I just defined an LFO variable that steps through different values for every tick. Then I added the LPF effect on the piano instrument and ‘looked’ at the current tick to decide what the cutoff frequency should be at any given time.

If you want to get more fancy with it you could create multiple LFO variable that are all modulating at different rates, then apply them to various effect parameters like amp, cutoff, resonance, phase, pan, etc… This will give a feeling that the instrument’s timbre is morphing over time.

# Coded by Viktor Pavlov
# Piano part from Arvo Pärt's "Spiegel im Spiegel"

use_bpm 80

set :sequence, []

# AUTOMATION:
lfo0 = (line 50, 100, inclusive: true, steps: 32).mirror

live_loop :receive_sequence do
  use_real_time
  seq = sync "/osc*/gen/sequence"
  s = sync "/osc*/gen/steps"
  set :sequence, seq.zip(s)
end

live_loop :metronome do
  sleep 1
end

with_fx :reverb, room: 1 do
  live_loop :piano_part, sync: :metronome do
    F = (ring :C5, :F5, :A5)
    Gm7 = (ring :As4, :F5, :G5)
    Bbmaj7 = (ring :D5, :A5, :Bb5)
    
    use_synth :piano
    use_synth_defaults sustain: 0.8, release: 0.2, hard: 0.1
    18.times do
      with_fx :lpf, cutoff: lfo0.look do
        play F.tick
        sleep 1
      end
    end
    6.times do
      play Gm7.tick
      sleep 1
    end
    24.times do
      play F.tick
      sleep 1
    end
    6.times do
      play Bbmaj7.tick
      sleep 1
    end
  end
  
  
  live_loop :play_gen_sequence, sync: :metronome do
    notes = get[:sequence] || []
    puts notes
    
    if notes.empty?
      sleep 1
    else
      notes.each do |note, step|
        use_synth :blade
        use_synth_defaults amp: 0.4, attack: step * 0.4, decay: step * 0.1,
          sustain: step * 0.3, release: step * 0.2, vibrato_rate: 7
        play note
        use_synth :square
        play note, amp: 0.03
        sleep step
      end
    end
  end
end
2 Likes

Thanks a lot! I will play around with it :slight_smile:

Hi, the code for this project together with a usage guide can be found here.

Would really appreciate any feedback if someone wants to play around with it and run it on their own machine :slight_smile: