General MIDI Electro

Hey all! I haven’t posted in a while. Been keeping myself busy with teaching and coding tunes. I’ve been spending a lot of time on MIDI, I’m really enjoying the generative abilities of SPi in conjunction with synth sound design. Since I can’t share that code with y’all, I decided to make a little electro tune with General MIDI to show some of the techniques I’ve been using in my creations. I’m happier with the results than I expected, considering it’s the generic midi sounds. If you have Windows this should work immediately. Otherwise you’re going to need to find a MIDI softsynth that will play GM/GS or play it over a keyboard that does GM and change the port variable.

port = "microsoft_gs_wavetable_synth"
#set up the channels and make sure everything is switched before continuing.
midi_pc 38, port: port, channel: 1
midi_pc 94, port: port, channel: 2
midi_pc 3, port: port, channel: 3
midi_pc 80, port: port, channel: 4
sleep 0.1

use_bpm 65

with_midi_defaults port: port, channel: 10 do
  live_loop :drums, delay: 8 do
    stop if tick > 165
    #First kick is set, next 3 are random
    midi 35
    sleep 0.125
    3.times do
      midi 36 if one_in(7)
      sleep 0.125
    end
    #Snare is set and alternates, claps are random
    midi [40, 38].ring.tick(:sn)
    sleep 0.125
    3.times do
      midi 39 if one_in(4)
      sleep 0.125
    end
  end
  
  live_loop :hats do
    stop if tick > 63
    #hats do their own thing in time. the strong beat is steady, the secondary beat is random.
    (range, 2, 15).choose.times do
      midi 44, vel: rrand_i(107, 127)
      sleep 0.125
      midi 42, vel: rrand_i(64, 96) if one_in(3)
      sleep 0.125
    end
    #play an open hat at the end of the line half the time. There is always a little bit of
    #space after a hat run to let them breathe for a second.
    midi 46, vel: rrand_i(64, 96) if one_in(2)
    sleep [0.25, 0.5, 0.75].choose
  end
  
end

with_midi_defaults port: port, channel: 1 do
  live_loop :bass, delay: 64 do
    #an end for the basslne
    if tick == 67
      midi 32, sustain: 0.25
      sleep 0.25
      midi 27, sustain: 0.125
      sleep 0.125
      midi 30, sustain: 0.125
      sleep 0.125
      midi 27, sustain: 0.25
      stop
    end
    #The bass does its thing in a relative pentatonic, no chord progression necessary.
    #sync if allows the bass to wander around for a while and then come back on key beats
    sync :drums if one_in(3)
    6.times do
      #I use nlen (note length) a lot to keep sustain/release relative to the sleep duration.
      #In this case we jump between a 75% and 100% gate randomly relative to the sleep.
      nlen = [0.125, 0.25, 0.375].choose
      midi (scale, 27, :minor_pentatonic).choose, sustain: nlen * [0.75, 1].choose
      sleep nlen
    end
  end
end

with_midi_defaults port: port, channel: 2 do
  live_loop :pads do
    stop if tick > 63
    #notes is the root of the stack.
    notes = [51, 49, 47, 46, 51, 49, 47, 49, 51, 49, 47, 46, 51, 49, 47, 53].ring
    midi notes.look , sustain: 2, vel: 90
    #note2 is a fifth up except on the final chord where it's raised a half step
    note2 = notes.look + 7
    note2 += 1 if look % 16 == 15
    midi note2 , sustain: 2, vel: 84
    #the last note is an octave higher
    midi notes.look + 12 , sustain: 2, vel: 78
    sleep 2
    
  end
end

with_midi_defaults port: port, channel: 3 do
  live_loop :chords, delay: 32 do
    stop if tick > 63
    #setting up the chord degrees and inversions for anything folowing the progression.
    deg = [1, 3, 4, 5].ring.look
    inv = [0, -1, -2, -2].ring.look
    2.times do
      #broadcast the degree and inversion to anything syncing
      cue :cho, deg: deg, invert: inv
      #this alternates between the set inversion and 2 inversions up
      notes = (chord_invert,(chord_degree, deg, 51, :minor, 3 ) , inv + [0, 2].ring.tick(:inv))
      notes.each do |n|
        midi n , sustain: 1, vel: 85
      end
      sleep 1
    end
  end
end

with_midi_defaults port: port, channel: 4 do
  live_loop :mel, delay: 96 do
    #end on the root of the tonic
    if tick > 17
      midi 63, sustain: 2, vel: 112
      stop
    end
    cho = sync :cho
    #never start right in time with the kick, always a slight delay
    sleep [ 0.25, 0.5].choose
    #long enough to make a phrase, short enough so that the line doesn't run into the wrong chord
    5.times do
      nlen = [0.25,  0.5, 0.75].choose
      notes = (chord_invert,(chord_degree, cho[:deg], 63, :minor, 4 ) , cho[:invert] + [0, 1, 2].choose)
      midi notes.choose , sustain: nlen * 1, vel: rrand_i(96, 112)
      sleep nlen
    end
  end
end

1 Like

Very nice!
For anyone wanting to use this on a Mac I suggest using the free vmpk (virtual midi piano keyboard) package. You can download it from http://vmpk.sourceforge.net/
You can then use the virtual IAC driver on the audio midi setup utility to create an interface that Sonic Pi can drive. (I created a port called SonicPi giving a device “IAC Driver SonicPi” in the device list on SonicPi. USe this as the port setting in the program instead of “microsoft_gs_wavetable_synth”
In vmpk select midi connections from the edit menu and set the MIDI IN driver to CoreMIDI
also set the Input MIDI COnnection to IAC Driver SonicPi
The MIDI OUT Driver should be set to FLiuidSynth
Works nicely.

1 Like

Thank you for that info. I wasn’t sure the exact synths on other platforms that would be an easy install. Been in Windows mode lately. :slight_smile: