Question of good Coding

Hi to all,

this is a more conceptual question concerning good (or at least decent) coding qualitites once you leave the mere area of spaghetti code (albeit within Sonic Pi). I do have a code example but nothing to really try out because the whole script is much bigger and I do not expect anyone to test my home-made software (as long as its not really running).

As I am trying to adapt my Live Looper to the Arturia Beatstep I also planed to go over it to make slimmer and better maintainable. I have e. g. several midi pads and knobs to watch, which I already managed to do in more efficient way.

Also I have to create several live_loops on the fly. The following example shows the playback live_loop for track1 (and I have 4 tracks to cover):

live_loop :play1 do
  on get(:arm_track1) do
    cue :rec
  on get(:play_track1) do
    time_warp get(:time_fix_play) do
      s = sample t1, amp: get(:vol_track1), hpf: get(:hpf_track1), lpf: get(:lpf_track1)
      set :t1, s
    end # time_warp
  sleep get(:len_track1)

So I thought I could create a function, which I can be called in 4.times to create 4 playback live_loops.

# get symbol pointers consisting of keyword and track index
define :get_pointer do | prefix, index |
  s = (prefix + index.to_s).to_sym

t1 = buffer[:track1, get(:len_track1)]
t2 = buffer[:track2, get(:len_track2)]

define :create_playback_loop do | buffer, index |
  live_loop get_pointer("play", index) do
    on get(get_pointer("arm_track", index)) do
      cue :rec
    on get(get_pointer("play_track", index)) do
      time_warp get(:time_fix_play) do
        #with_fx :sound_out_stereo, output: 3 do
        s = sample buffer, amp: get(get_pointer("vol_track", index)), hpf: get(get_pointer("hpf_track", index)), lpf: get(get_pointer("lpf_track", index))
        set get_pointer("t", index), s
      end # time_warp
    sleep get(get_pointer("len_track", index))

# Create the play back live_loops
# Could later be done with a loop
create_playback_loop(t1, 1)
create_playback_loop(t2, 2)

The code runs (no syntax or run_time error) but it does not work as expected. I do have not the slightest idea what’s going wrong but I suspect there might be a very basic design flaw in it. So my hope is that someone more proficient as me could be able to hint me into the right direction or confirm that this basically is a valid approach (and I have to look for the reason it does not work as expected elsewhere).


H Martin
I’ll have to dig out your live_looper stuff gain to refresh my memory how it was working.
However looking at the idea you are trying to achieve it reminded me of the program I did for a TouchOSC (and touch keyboard) driven project to play samples. In this I tried to produce routines that were reusable in different situations rather than defining them many times over. It may be that some of the ideas in the code are helpful to you.
gist was here and videe of TouchOSC version of the project here

I remember it was quite tricky to achieve!

Hi @robin.newman

thanks, that encourages me :wink: I will have a look at your code, especially at the reusable parts!


PS.: I know that my request is very general, so no specific feedback expectable. But to look at other’s code will be helpful.


definitely I am progressing but stumbled over a new question:

I would like to my application device independant (as far as possible). Therefore I need a configuration part at the top of the script. I have some data with will not change, such as Midi controller numbers, once the script has started. I can easily do that with e. g.:

set :midis, [
  [52, 56, 16, 28, 20], # track one: controllers for play and record toggle, amp, lpf and hpf cutoff
  [53, 57, 16, 29, 21] # track two... and more to come

I ran into a problem as I wanted to initially store data for tracks 1 to n:

set :tracks, [
      [:track1, 0, 8, 1, 130, 0, false, false], # track 1
      [:track2, 1, 8, 1, 130, 0, false, false]  # track 2 ...

This list does contains some data which won’t change during runtime (such as length of the recorded track) but otherwise data which I will have to change such as amp, cutoff values or boolean vars to indicate whether the track should be play or not.

No I know (or better: refreshed my knowledge) about immutablity, so I won’t be able to do something like:

set :tracks[1][7], false

Otherwise the storage of this data would be very handy because I will have write functions that will construct dynamically the need live_loops such as:

define :create_playback_loop do | par |
  get(:tracks).size.times do | i |
    live_loop get(:tracks)[i][0] do # not sure if that works at all, will solve this later...
      # do stuff and get values for 4 or more live_loops to be constructed

I am aware that the code above might not work; it is just a sketch to get my idea across. The final goal would be that I can e. g. another track just by defining new controllers and extending the tracks array. So if I can not use get/set what is an possible way without leaving the realm of Sonic Pi language context?

I hope I did make myself clear. Otherwise I can go into detail.

Any suggestions?