A recipe for 8 beats

trying to boil my method down further. hardly feels like composing music anymore, just kind of describe a vague structure and the rest falls in place. hopefully the comments help explain the process better.

# arbitrary settings, sounds nice to me
use_bpm 99
r = :f1 # root note

# drums n bass in the same loop
# doing an 8 beat pattern
live_loop :dnb do
  
  # first 4 beats are basic
  use_synth :tri
  play r, release: 2.1
  sample :bd_tek
  sleep 2
  sample :sn_dolf, amp: 0.5
  sleep 2
  
  # downbeat of 2nd bar
  play r, release: 0.5
  sample :bd_tek
  
  # next 4 are random snare fills and bass runs using one_in
  sleep 0.5
  14.times do
    play (chord r, :major7).pick, release: 0.3, on: (one_in 5)
    sample :sn_dolf, amp: 0.3, beat_stretch: 0.2 + rand(0.8), on: (one_in 5)
    sleep 0.25
  end
end

# slicer on everything else to cut a hole on the downbeat for a bigger bass and kick
with_fx :slicer, phase: 4, pulse_width: 0.01, smooth_down: 0.02, smooth_up: 3.5, invert_wave: 1 do
  with_fx :reverb, room: 0.9, mix: 0.8 do
    # air is the very slow atttack, harmonic element that fills space
    live_loop :air do
      use_synth :mod_tri
      use_synth_defaults mod_range: 0.4, mod_phase: 1/9.0, mod_phase_slide: 8, mod_wave: 2
      # pick a random note from the scale, turn it into a big chord
      n = play (chord (scale r+24, :major_pentatonic, num_octaves: 2).pick[0], :major, num_octaves: 2), attack: 6, amp: 0.5
      sleep 1
      # gradually slows the vibrato down
      control n, mod_phase: 1
      sleep 7
    end
  end
  
  # a smaller reverb space than air
  with_fx :reverb, room: 0.7, mix: 0.5 do
    # arp is the melodic element, an arppeggio that occasionally reaches higher octaves
    live_loop :arp do
      32.times do
        use_synth :mod_fm
        # random params for weird modulations
        use_synth_defaults divisor: 0.1+rand(0.6), release: 0.4, mod_phase: 1/(1.0+rand_i(3)), mod_wave: 2, mod_range: 0.5
        # the random octave range is the melodic variation
        play (chord r+24, :major7, num_octaves: 1+rand(3)).tick, amp: 0.01 + rand(0.5)
        sleep 0.5
      end
      # reset to root note on downbeat
      tick_reset
    end
    
    # splash on the downbeat to give it some sparkle
    live_loop :splash do
      sample :drum_splash_soft, amp: 0.3
      sleep 8
    end
    # a hat to bring out a little syncopation
    live_loop :hat do
      sleep 1
      sample :drum_cymbal_closed, amp: 0.7
      sleep 1
    end
  end
end
1 Like

This sounds amazing. Keep up the good work.

One old idea is to generate notes (or chords, rhythms, etc.) using a combinatorial constraint solver. For instance, let’s “compose” some notes:

N = [40, 52, 50, 47, 54, 47, 50, 52, 40, 52, 50, 47, 54, 47, 50, 52]

To make it more interesting, we “tell” the computer to automatically add a couple of voices (this is a quickly coded stupid example, but you get the philosophy, namely to give the computer instructions rather than specific notes):

require 'winston'

use_bpm 80

define :myplay do |n, t|
  a = 0.95
  use_synth :dsaw
  use_synth_defaults amp: 0.6, cutoff: 100
  play n, sustain: a*t, release: (1.0-a)*t
  sleep t
end

NNN = [:e2, :e3, :d3, :b2, :fs3, :b2, :d3, :e3].map {|n| note(n)}*2

CONS = [3,4,7,8,9,12,15,16]

define :sol1 do
  output = Array.new
  
  csp = Winston::CSP.new
  
  D =  (scale :e2, :minor, num_octaves: 3).to_a.map { |x| note(x) }
  
  (0..NNN.size-1).each do |i|
    csp.add_variable({ n: i}, domain: D)
    csp.add_variable({ m: i}, domain: D)
  end
  variables = csp.variables.keys
  NNN.each_with_index do |n,i|
    the_note = variables.select { |x| x[:n] == i }
    the_note2 = variables.select { |x| x[:m] == i }
    
    csp.add_constraint(the_note) { |x| CONS.include?((x-n)) }
    csp.add_constraint(the_note2) { |x| (CONS+CONS.map {|n| n+12}).include?((x-n)) }
    csp.add_constraint(the_note, the_note2) { |x,y| CONS.include?((y-x)) }
  end
  
  (1..NNN.size-1).each do |i|
    prev_note = variables.select { |x| x[:n] == i-1 }
    next_note = variables.select { |x| x[:n] == i }
    prev_note2 = variables.select { |x| x[:m] == i-1 }
    next_note2 = variables.select { |x| x[:m] == i }
    csp.add_constraint(prev_note, next_note) { |x,y| [1,2,3,4,5,7,12].include?((x-y).abs) }
    csp.add_constraint(prev_note2, next_note2) { |x,y| [1,2,3,4,5,7,12].include?((x-y).abs) }
    
    csp.add_constraint(prev_note, next_note, prev_note2, next_note2) { |x,y,z,w|
    !(((x-y)%12==7)&&((z-w)%12==7)) }
    csp.add_constraint(prev_note, next_note, prev_note2, next_note2) { |x,y,z,w|
    !(((x-y)%12==0)&&((z-w)%12==0)) }
    csp.add_constraint(prev_note, next_note) { |z,w| x = NNN[i-1]; y = NNN[i]; !(((x-y)%12==7)&&((z-w)%12==7)) }
    csp.add_constraint(prev_note2, next_note2) { |z,w| x = NNN[i-1]; y = NNN[i]; !(((x-y)%12==7)&&((z-w)%12==7)) }
    csp.add_constraint(prev_note, next_note) { |z,w| x = NNN[i-1]; y = NNN[i]; !(((x-y)%12==0)&&((z-w)%12==0)) }
    csp.add_constraint(prev_note2, next_note2) { |z,w| x = NNN[i-1]; y = NNN[i]; !(((x-y)%12==0)&&((z-w)%12==0)) }
    
  end
  sol = csp.solve
  output = Array.new
  output2 = Array.new
  (0..NNN.size-1).each do |i|
    a_note = variables.select { |x| x[:n] == i }
    b_note = variables.select { |x| x[:m] == i }
    output.append sol[a_note[0]]
    output2.append sol[b_note[0]]
  end
  return [output, output2]
end
ss = sol1
NNN.each { |n| myplay n, 0.25 }
in_thread { NNN.each { |n| myplay n, 0.25 }}
ss[0].each { |n| myplay n,0.25 }

in_thread { NNN.each { |n| myplay n, 0.25 }}
in_thread { ss[0].each { |n| myplay n,0.25 }}
ss[1].each { |n| myplay n,0.25 }
stop

What is ‘winston’? I get an error and thread death on line 1. I tried doing ‘gem install winston’ in Terminal and then restarting Sonic Pi, but, same result

It is a random gem I grabbed when looking for a constraint solver for the purposes of creating a quick example. There is probably a much better gem to use, but we get some sounds :slight_smile: You need to make sure it shows up in your gem path, e.g. try starting Sonic Pi from the same terminal where the gem is installed and shows up in gem list.

For a concrete example, I gave it the original notes

 [40, 42, 43, 45, 43, 42, 40, 40, 40, 42, 43, 45, 43, 42, 40, 40]

and, with the above sample constraints (E minor scale, consonant intervals between voices, no parallel fifths…) it comes up with the (slightly unfortunate, in fact I see a number of bugs in the code I posted before) solution

 ├─ [40, 42, 43, 45, 43, 42, 40, 40, 40, 42, 43, 45, 43, 42, 40, 40]
 ├─ [43, 45, 47, 48, 47, 45, 43, 47, 43, 45, 47, 48, 47, 45, 43, 47]
 ├─ [52, 57, 55, 60, 55, 57, 52, 55, 52, 57, 55, 60, 55, 57, 52, 55]

Anyway, with some proper coding some old examples could be adapted into Ruby, e.g. see here: Strasheela Examples

This is all merely supposed to be in the original poster’s philosophy of “describe a structure” and let the computer fill in the notes