Simple Function that Accept Notes or Samples as Arguments?

I wrote code to play a rhythm. I want to reuse this code and use two different live_loops to simultaneously:

a. play a bassline by passing a sample as the argument to my function, and
b. play a melody by passing notes as the argument to my function

Unfortunately, if you are playing a note you use the keyword “play” and if you are playing a sample you use the keyword “sample”. Is there any way to write a function that will be able to, depending on the argument, produce the desired sound at the function-specified rhythm? (See code below)

# Code below is NOT functioning as desired
use_bpm: 140
define :songrhythm do |a, b, c, d, e, f, x|

play a, amp: x, sustain: 1.5
  sleep 1.5
  play b, amp: x, sustain: 1.5
  sleep 1.5
  play c, amp: x, sustain: 1.5
  sleep 1.5
  play d, amp: x, sustain: 0.5
  sleep 0.5
  play e, amp: x, sustain: 1
  sleep 1
  play f, amp: x, sustain: 1
  sleep 2
end

live_loop :bass do
  s = sample :drum_bass_hard
  songrhythm s, s,  s,  s,  s,  s, 4
end

live_loop :melody do
  use_synth :fm
  songrhythm :b2, :b2, :b2, :g2, :g2, :g2, 1
end

Here is a working version of the code where I’ve used a arrays and iterated through them. The user must specify “n” for note or “s” for sample as the final argument and a conditional statement is used to run the appropriate code.

I am working on a project with students and would like a solution that is a little less complex; any other solutions for this problem that would help me emphasize the idea of taking a block of code and reusing it by defining a function would be appreciated!

# Code below IS functioning as desired, but more complex than I would like for teaching   
use_bpm 140

s =  :drum_bass_hard
notes = [:b2, :b2, :b2, :g2, :g2, :g2]
samples = [s, s, s, s, s, s]
duration = [1.5, 1.5, 1.5, 0.5, 1, 2]


define :songrhythm do |list1, list2, amplitude, note_or_sample|
  i = 0
  if note_or_sample == "n"
    use_synth :fm
    while (i < list1.length)
      play list1[i], amp: amplitude, sustain: list2[i]
      sleep list2[i]
      i += 1
    end
  elsif note_or_sample == "s"
    while (i < list1.length)
      sample list1[i], amp: amplitude, sustain: list2[i]
      sleep list2[i]
      i += 1
    end
  else
    print "Please specify whether you are using a note or sample in the function!"
  end
end

live_loop :bass do #sample of the kick drum
  songrhythm samples, duration, 2, "s"
end

live_loop :melody do #sample of the bass line
  songrhythm notes, duration, 2, "n"
end

One way to automate it might be to check for the data type coming in.

a = 64
if a.is_a?(Numeric) then
  play a
else
  sample a
end

You’d need to only use MIDI note numbers because the note name label would fail the check.

See how you like this. I refactored some things to make it more ruby/SPi idiomatic. Now the only concern is that checking data types isn’t really within SPi’s scope, but if it works, run with it.

use_bpm 140

s =  :drum_bass_hard
notes = [35, 35, 35, 31, 31, 31]
samples = [s,s,s,s,s,s]
duration = [1.5, 1.5, 1.5, 0.5, 1, 2]

define :songrhythm do |sounds, timing, amplitude|
  use_synth :tb303
  sounds.each do |i|
    if i.is_a?(Numeric) then
      play i, amp: amplitude, release: timing.ring.tick
    else
      sample i, amp: amplitude, sustain: timing.ring.tick
    end
    sleep timing.ring.look
  end
end

live_loop :bass do #sample of the kick drum
  songrhythm samples, duration, 2
end

live_loop :melody do #sample of the bass line
  songrhythm notes, duration, 2
end

The following code will also run with notes as symbols. I defined a function playOrSample :

use_bpm 140

define :playOrSample do | ns, a = 1, s = 0 |
  isnote = true
  # decide: note or sample?  may be a better way...
  begin
    ns.to_i
  rescue
    isnote = false
  end  
  if isnote
    play ns , amp: a, sustain: s
  else
    sample ns , amp: a, sustain: s    
  end
end


define :songrhythm do |a, b, c, d, e, f, x|
  
  playOrSample a, x,  1.5
  sleep 1.5
  playOrSample b, x,   1.5
  sleep 1.5
  playOrSample c,  x,  1.5
  sleep 1.5
  playOrSample d,   x,  0.5
  sleep 0.5
  playOrSample e,  x,  1
  sleep 1
  playOrSample f,  x,   1
  sleep 2
end

live_loop :bass do
  s = sample :drum_bass_hard
  songrhythm s, s,  s,  s,  s,  s, 4
end

live_loop :melody do
  use_synth :fm
  songrhythm :b2, :b2, :b2, :g2, :g2, :g2, 1
end

Hi,

Everyone has their own style… in my case, I dont think I’d want to write
a single function to deal witth 2 such diverse methods, especially if I was
teaching other people.

This works just as well, and is probably much more understandable to people
who are just starting out…

Just my 10p…

Eli…

define :playsample do |name|
  sample name
end

define :playnote do |note, duration|
  play note,sustain: duration
end

playsample :ambi_choir
sleep 8
playnote 62, 2
1 Like