Music Generator integration with Sonic PI

Hi :slight_smile: ,

I am working on a thesis project, that would use algorithmic music generation in context of live coding, by building an application, that would generate variations on melodies and beats, and integrate it with Sonic PI.

At this stage, I have a generator, that produces variations on a single voice MIDI input, based on nth-order Markov chains. The result is then sent to Sonic PI, utilzing the OSC protocol.

Sonic PI code:

live_loop :foo2 do
  with_fx :reverb, mix: 0.2, room: 0.8 do
    use_synth :fm
    use_real_time
    note = sync "/osc*/melody/notes"
    play note
  end
end

Sending OSC messages:

for (const [idx, [pitch, quantizedSteps]] of notes.entries()) {
  this.oscClient.send(pitch);
  /* eslint-disable-next-line no-await-in-loop */
  await sleep((quantizedSteps / stepsPerQuater) * 1000);
  bar.update(idx + 1);
}

Currently, messages are played, when they are received by Sonic PI and I was wondering what would be the options of syncing the incoming messages to the current beat/loop(s)/metronome playing in Sonic PI?

Or, what could be done alternatively to sync the generator with Sonic PI?

Thanks in advance :slight_smile:

Does adding a second sync under the one that receives MIDI events to wait for a metronome (or similar) cue help? eg:

    note = sync "/osc*/melody/notes"
    sync :metronome
    play note

(This would of course need slightly different handling depending on whether the idea is to receive a single generated note at a time, or a sequence of notes at a time).

@ethancrawford Thank you for your reply!

I actually changed the way the generated notes are being sent. Instead of sending notes sequentially in real time, I am sending the whole generated sequence in two messages - notes and durations. Additionally, I am syncing the loop that plays the incoming sequence with the beat. Seems to be working :slight_smile:

use_bpm 120

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

live_loop :beat do
  sample :drum_bass_soft
  sleep 1
end

live_loop :play_gen_sequence, sync: :beat do
  use_synth :hoover
  gen_notes = get[:sequence] || []
  gen_steps = get[:steps] || []
  notes = gen_notes.zip(gen_steps)
  puts notes
  
  if notes.empty?
    sleep 1
  else
    notes.each do |note, step|
      play note, release: 1, amp: 0.3
      sleep step
    end
  end
end
1 Like

Yes, that makes sense! :slight_smile: