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