Rubber Soul - 4 Channel Mixer & Sequencer

Hi folks,

Every few months I get this terrible urge to code something huge and useless… think of
it as a time-wasting exercise, if you want.

So, this time around, here we have a little mixer and sequencer. I know Robin’s work on
OSC/TouchOS knocks this out the window, but I never said it was good… just that it takes up
my time… :slight_smile:

Couple of tiny glitches to work out, which will no doubt plague me for a couple of weeks.

EDIT: Fixed an errant ‘tick’ that was setting the bpm incorrectly.

Eli…

#Rubber Soul - 4 Channel Mixer & Sequencer
#Eli... March 2018.
#======================================================================================================================================
# Use: Assign an instrument to channel 1-4, choose a bpm, drum and beat pattern, then run.
#      You can have multiple instruments on the same channel. Volume defaults to 3 on all
#      channels unless you are using the sequencer, in which case it uses the volume set
#      in the sequncer list. If a channel has no instruments attached, nothing will play in
#      the sequencer for that channel... Still a Work in Progess / Time Wasting Exercise...
#
#  Enjoy... or laugh silently behind  your hand... :)
#  Eli...
#======================================================================================================================================
#Set BPM  60 90 100 110 120
#==========================
set_bpm = [0, 0, 0,  0,  1]
#==========================
# Instrument  Channel
#====================
kick=            2
hats=            2
rim=             2
woodblock=       0
bass=            0
snap1=           2
snap2=           0
chime=           0
background1=     0
tune1=           1
#===========================================
#  Drum:   :bd_ada :bd_fat :bd_boom :bd_808
#===========================================
drum_select = [1,      0,       0,      0]
#===========================================
# beats/bar        [1, 2, 3, 4]
#==============================
beat_select =      [0, 1, 0, 1]
hat_select =       [0, 1, 1, 0]
rim_select =       [0, 0, 0, 0]
snap1_select =     [0, 0, 1, 0]
snap2_select =     [1, 0, 0, 0]
chime_select =     [0, 1, 0, 0]
woodblock_select = [0, 0, 0, 0]
#==============================

ON=1
OFF=0
SINGLE=0
LOOP=1

SEQUENCER = ON
MODE = LOOP
#===============================================================
bar   =  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
#===============================================================
volume1 = [3, 3, 3, 2, 1, 0, 0, 0, 0, 0,  0,  0,  0,  0,  0,  0]
volume2 = [0, 0, 0, 0, 0, 3, 3, 3, 0, 0,  0,  0,  0,  0,  0,  0]
volume3 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0,  0,  0]
volume4 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0,  0,  0]
#==============================================================

tune1_chords = (ring :r,
                (chord_degree :i,:A3,:minor,3),(chord_invert (chord :G3,:major),1),
                (chord_degree :iv,:D3,:minor,4),(chord_degree :i,:A3,:minor),
                )

set_volume! 5

# bass notes/rests
bass_notes = (ring :r, :r, :c2, :d3, :r, :f3, :r, :a1, :f2)
#default drum
drum_selected = :bd_ada

channel = [0, 0, 0, 0]
mixer   = [0, 0, 0, 0]
mix     = [0, 0, 0, 0]

define :start_channel do |i=1,x=0|
  if i>0 && i<5 then
    channel[i] = 2
    if x > 0 then
      mixer[i] = x
    end
    set :ch[i], 2
  else
    puts "Channel number outside 1-4"
  end
end

define :oneshot_channel do |i|
  if i>0 && i<5 then
    channel[i] = 1
    set :ch[i],1
  else
    puts "Channel number outside 1-4"
  end
end

define :mute_channel do |i|
  if i>0 && i<5 then
    channel[i] = 0
    set :ch[i],0
  else
    puts "Channel number outside 1-4"
  end
end

define :stop_channel do |i|
  if i>0 && i<5 then
    channel[i] = 0
    mixer[i] = 0
    set :ch[i], 0
  else
    puts "Channel number outside 1-4"
  end
end

define :loop_channel do |i|
  if i>0 && i<5 then
    channel[i] = 0
    set :ch[i],2
  else
    puts "Channel number outside 1-12"
  end
end

define :default_mixer_values do
  set :mix [1], 0
  set :mix [2], 0
  set :mix [3], 0
  set :mix [4], 0
end
define :set_bpm do
  bpms=[60,90,100,110,120].ring
  tick_reset
  set_bpm.each do |x|
    tick
    set :bpm, bpms.look if x == 1
    puts x
    puts bpms.look
  end
end

#This is a hack to restart tune1/background1
# with a new bpm, instead of starting a new instance.
bpms=[60,90,100,110,120].ring
temp=0
tick_reset
if tune1 > 0
  temp = tune1
  stop_channel tune1
end
set_bpm.each do |x|
  tick
  set :bpm, bpms.look if x == 1
end
if temp >0
  start_channel tune1, 3
end
tick_reset
if background1 > 0
  temp = background1
  stop_channel background1
end
  tick
set_bpm.each do |x|
  set :bpm, bpms.look if x == 1
end
if temp >0
  start_channel background1, 3
end

live_loop :switch_channels do
  instruments = [kick,hats,rim,woodblock,bass,snap1,snap2,chime,background1,tune1]
  instruments.each do |x|
    start_channel x, 3 if x > 0
  end
  sleep 0.25
end

set_bpm

live_loop :beats do
  speed = get :bpm
  use_bpm speed
  sleep 1
end

live_loop :bar do
  sync :beats
  sleep 4
end

live_loop :do_sequencer do
  if SEQUENCER == ON then
    if MODE == LOOP
      sequence=2
    else
      sequence=1
    end
    if sequence>0 then
      bar.each do |x|
        sync :bar
        finish=x-1
        set :mix [1], volume1[x-1]
        set :mix [2], volume2[x-1]
        set :mix [3], volume3[x-1]
        set :mix [4], volume4[x-1]
        puts volume1[x-1], volume2[x-1], volume3[x-1],volume4[x-1]
        if finish == 15 then
          if MODE == SINGLE then
            sequence = 0
          end
        end
      end
    end
    if sequence == 1 then
      sequence = 0
    end
  else
    sleep 0.25
  end
end

live_loop :kick do
  if channel[kick]>0 then
    if SEQUENCER == ON then
      this_vol = get :mix [kick]
    else
      this_vol = 3
    end
    drum_selected = :bd_ada if drum_select[1] == 1
    drum_selected = :bd_fat if drum_select[2] == 1
    drum_selected = :bd_boom if drum_select[3] == 1
    drum_selected = :bd_808 if drum_select[4] == 1
    beat_select.each do |x|
      sync :beats
      sample drum_selected,amp: this_vol if x == 1
    end
  else
    sleep 0.25
  end
end

live_loop :hats do
  if channel[hats]>0 then
    if SEQUENCER == ON then
      this_vol = get :mix [hats]
    else
      this_vol = 3
    end
    hat_select.each do |x|
      sync :beats
      sample :sn_dolf, sustain: 0, release: 0.08,amp: this_vol if x == 1
    end
    if channel[hats] == 1 then
      channel[hats] = 0
    end
  else
    channel[hats] = 0
    sleep 0.5
  end
end

with_fx :rhpf, res: 0.85, cutoff: 118, amp: 0.7 do
  live_loop :woodblock do
    if channel[woodblock]>0 then
      if SEQUENCER == ON then
        this_vol = get :mix [woodblock]
      else
        this_vol = 3
      end
      woodblock_select.each do |x|
        sync :beats
        sample :elec_wood, sustain: 0, release: 0.08,amp: this_vol if x == 1
      end
      if channel[woodblock] == 1 then
        channel[woodblock] = 0
      end
    else
      channel[woodblock] = 0
      sleep 0.5
    end
  end
end

with_fx :rhpf, res: 0.85, cutoff: 118, amp: 0.7 do
  with_fx :distortion do
    live_loop :rim do
      if channel[rim]>0 then
        if SEQUENCER == ON then
          this_vol = get :mix [rim]
        else
          this_vol = 3
        end
        rim_select.each do |x|
          sync :beats
          sample :elec_blip, sustain: 0.006, cutoff:110, rate: 0.8,amp: this_vol if x == 1
        end
        if channel[rim] == 1 then
          channel[rim] = 0
        end
      else
        channel[rim] = 0
        sleep 0.5
      end
    end
  end
end

live_loop :bass do
  speed = get :bpm
  use_bpm speed
  if channel[bass]>0 then
    if SEQUENCER == ON then
      this_vol = get :mix [bass]
    else
      this_vol = 3
    end
    if rand(1) > 0.75
      bass_notes = (ring :r, :r, :c2, :d3, :r, :f3, :r, :a1, :f2)
    else
      bass_notes=bass_notes.shuffle
    end
    use_synth :fm
    use_transpose +0
    use_synth_defaults release: 0.125 + rrand(0, 0.2), amp: this_vol, pan: rrand(-0.5, 0.5)
    play bass_notes.look, cutoff: rrand(30, 130)
    sleep 0.25
    tick
    if channel[bass] == 1 then
      channel[bass] = 0
    end
  else
    channel[bass] = 0
    sleep 0.5
  end
end

live_loop :snap1 do
  speed = get :bpm
  use_bpm speed
  if channel[snap1]>0 then
    if SEQUENCER == ON then
      this_vol = get :mix [snap1]
    else
      this_vol = 3
    end
    snap1_select.each do |x|
      sync :beats
      sample :perc_snap, sustain: 0, release: 0.08,amp: this_vol if x == 1
    end
    if channel[snap1] == 1 then
      channel[snap1] = 0
    end
  else
    channel[snap1] = 0
    sleep 0.5
  end
end

live_loop :snap2 do
  speed = get :bpm
  use_bpm speed
  if channel[snap2]>0 then
    if SEQUENCER == ON then
      this_vol = get :mix [snap2]
    else
      this_vol = 3
    end
    snap2_select.each do |x|
      sync :beats
      sample :perc_snap2, sustain: 0, release: 0.08,amp: this_vol if x == 1
    end
    if channel[snap2] == 1 then
      channel[snap2] = 0
    end
  else
    channel[snap2] = 0
    sleep 0.5
  end
end

live_loop :chime do
  speed = get :bpm
  use_bpm speed
  if channel[chime]>0 then
    if SEQUENCER == ON then
      this_vol = get :mix [chime]
    else
      this_vol = 3
    end
    chime_select.each do |x|
      sync :beats
      sample :elec_chime,amp: this_vol if x == 1
    end
    if channel[chime] == 1 then
      channel[chime] = 0
    end
  else
    channel [chime] = 0
    sleep 0.5
  end
end

with_fx :flanger, stereo_invert_wave: 1, feedback: 0.625, amp: 0.3 do
  with_fx :echo, mix: 0.8, phase: 0.75, decay: 4 do
    live_loop :background1 do
      speed = get :bpm
      use_bpm speed / 2.0
      if channel[background1]>0 then
        if SEQUENCER == ON then
          this_vol = get :mix [background1]
        else
          this_vol = 0.5
        end
        use_synth :tri
        mynote = (note_range, :g2, :g4, pitches: (scale, :g6, :minor_pentatonic))
        play mynote.choose,amp: this_vol
        sleep 0.25
        if channel[background1] == 1 then
          channel[background1] = 0
        end
      else
        channel[background1] = 0
        sleep 0.5
      end
    end
  end
end

live_loop :tune1 do
  speed = get :bpm
  use_bpm speed
  if channel[tune1]>0 then
    if SEQUENCER == ON then
      this_vol = get :mix [tune1]
    else
      this_vol = 0.5
    end
    use_synth :prophet
    use_synth_defaults attack: 0.05, sustain: 0.5, release: 0.75,
      cutoff: 80,amp: this_vol
    with_fx :flanger do
      tune1_chords=tune1_chords.shuffle
      mychord = tune1_chords.choose
      if mychord == :r then
        sleep 0.5
      else
        dur = [0.5,1].choose
        play mychord, release: dur
        sleep dur
        if channel[tune1] == 1 then
          channel[tune1] = 0
        end
      end
    end
  else
    channel[tune1] = 0
    sleep 0.25
  end
end