Is there a function for "solo" for a block?

I love that everyone gives their own little recipe!

I played around the idea of getting better track controls, this is what I have:

# you start by configuring your tracks (easier to remember with names)
tracks = { lead: true, pad: true, beat: true }

solo is a pretty simple helper function

def solo! tracks, name
  # go through each track make it true or false depending on
  # the current track being the one you want to solo
  tracks.each do |key, value|
    tracks[key] = key == name
  end
end

mute is even simpler

def mute! tracks, name
  tracks[name] = false
end

I got fancy and made a function that checks if the track is on, it’s completely optional.

# you have to add a fallback duration equivalent to your loop length:
# in case the track is turned off it sleeps for the loop duration
def track_switch is_on, duration
  if is_on
    yield
  else
    sleep duration
  end
end

Then you can use the controls pretty easily! Just comment and uncomment the lines and you’re set :wink:

solo! tracks, :pad
# or
mute! tracks, :lead
# etc.

I think it works pretty well! I haven’t paid attention to the sync stuff though but you can adapt from earlier answers if necessary.
The only thing you need to be careful about is the order: if you call for something before you define it the editor won’t be too happy.

The whole thing with a few example tracks you can play around with:

track switch, solo, mute and 3 tracks
# conf
tracks = { lead: true, pad: true, beat: true }

# helpers
def solo! tracks, name
  tracks.each { |key, value| tracks[key] = key == name }
end

def mute! tracks, name
  tracks[name] = false
end

def track_switch is_on, duration
  if is_on
    yield
  else
    sleep duration
  end
end

# tracks
solo! tracks, :pad

live_loop :lead_loop do
  track_switch tracks[:lead], 2 do
    play_pattern_timed scale(:e4, :mixolydian).shuffle,
      [2/3.0, 1/3.0, 2/3.0, 1/3.0], amp: 0.2
  end
end

live_loop :pad_loop do
  track_switch tracks[:pad], 1 do
    sleep 1/3.0
    play chord(:e3, :major), decay: 1, pitch: choose([0, 3, -2])
    sleep 2/3.0
  end
end

live_loop :beat_loop do
  track_switch tracks[:beat], 1/3.0 do
    sample choose([:perc_snap, :perc_snap2]), amp: rrand(0.3, 0.8)
    sleep 1/3.0
  end
end

EDIT: made the tracks a bit more melodic

EDIT 2: if you want to reuse these all the time, you can make this into a little lib somewhere on your system, ex:

run_file "{path to my lib}/track.rb"
2 Likes

Hello @pebblepoison and thanks to share your proposition…and yes it’s great that each one can suggets his own recipe!! Yours code is interesting also since you can here choose between “switch”, “mute” or “solo” a track (or tracks?) if I understand correctly your post.
But I feel stupid here, what are key and value (false, true?) ?

tracks.each { |key, value| tracks[key] = key == name }

Thnaks a lot for your post! :smiley:

Hi! Sorry I might have gone a little fast on the explanations :sweat_smile:

track_switch is just a name I gave my function to remember that it could act like a switch (example: a light switch) based on whether the track is true or false. It’s not an additional thing, it’s the mechanism for the other two.

Ok, lets decompose this. First, tracks is a ruby Hash, a sort of dictionary where each key matches a value. To “solo” a track, we need to make them all silent except the one we target. To do that, we use the hash method .each that allows us to loop through each key / value pair.

tracks.each do |key, value|
  print key, value # :lead, true then :pad, true then :beat, true
                   # based on the hash I made as an example
end

What do we want to tell the tracks? Well, if you’re equal to the track I’m soloing, become true! If you’re not become false. It can look like that:

tracks.each do |key, value|
  if key == name # is :lead == :pad then :pad == :pad then :beat == :pad
    tracks[key] = true
  else
    tracks[key] = false
  end
end

Bu something bothered me about it. Take a look:

tracks.each do |key, value|
  if key == name        # <- when true
    tracks[key] = true  # <- become true
  else                  # <- when false
    tracks[key] = false # <- become false
  end
end

A bit redundant, isn’t it? :wink: Because key == name returns the value we want, we can assign it directly:

tracks.each do |key, value|
  tracks[key] = key == name # <- when true: become true, when false: become false
end

Hope that cleared things up a little bit!

1 Like

Great explanations @pebblepoison thnx a lot, yes it’s clear now and also new for me this way of doing it…Do you use it personnaly in your compositions when you need to mute or soloing?
In fact, behinf this question there is another one: Is it possible to trigger them (mute, solo) at different points in the composition? To choose when to mute or soloing the track? With “delay” f.i?
:blush:

Hey! To be honest I just discovered Sonic Pi last week-end, so while I am a developer and amateur musician I’m mostly playing around the code!

I have used the mute! and solo! in the way they are used in sequencers: as a way to isolate a track to better hear it, not necessarily as a part of a composition but more in a live loop in which you rerun your changes. You can do that, but you might need to add ‘unsolo!’ and ‘unmute!’ functions which I do not need with my workflow.

The way I have used them:

  • I want to fine-tune a loop while the code is playing? I uncomment solo! and press run again.
  • I’m done? I comment solo! and press run again. The definition of tracks overrides the state and all the tracks become audible again.

mute works in the same way.

3 Likes

I’m late to the party here. It’s a fascinating thread, and I’ve learned alot.
But I’m still perplexed by “yield.” I read a bit in a ruby tutorial, and I still don’t understand its use. I vaguely get that it’s a placeholder to plug other code into, but how does one do so – and why?
Thanks!

Hi, yield is a keyword used when you want to generalize a function.

In this example, I want to make something happen when the track is on, but I don’t know what that something is gonna be in advance. It’s even likely to change every track!

So the definition of track_switch yields, and then when we use the function, we add a block to it.

track_switch tracks[:sometrack], somenumber do
   # the block is actually track_switch's third argument, sort of
 end

I think I get it. I’ll have to mull at this one, and try my hand at it to really grok.

Hi @pebblepoison thanks for the explanation but I am not sure
to understand very well, is it possible to have a concrete example?
Thanks a lot :smiley: