Since I plan to use Sonic Pi in a rather “traditional” way for the moment (to make covers where I trigger sequences, as opposed to a mainly algorithmic approach), I find myself using lots of functions…
But they all share the same general basic shape…
So I was wondering if it is possible to built generalist functions (Something I’d call like melodies_in_general, or drums_in_general), and make particular instances of those super functions, when I need to add something to my song… And then call those particular instances, instead of their parent function.
But right now, I have no idea how I’d do that, or even if it is possible, since calling a function depends on the name you give it at first…
It’s not about how to give them arguments/parameters… My problem is I don’t how I would name them, to differentiate them from the generalist form.
Any ideas please?
If the way I formulate the question is not clear enough, I’ll be happy to try again…
Cheers!
(PS: My first ever Sonic Pi project is taking longer than I expected, because the song I chose seems to be way less “approachable” than I first thought… It’s a very loopy song, so I thought I’d be easy to replicate… Surprise… It’s not! But I’ll get there eventually, I hope.)
I guess my first question would be what your other programming background might be? It sounds like you are looking for inheritance (which is at the class level not the function level) or composition (which might fit better into the overall paradigm of Sonic Pi).
I have zero background in programming (Just bought the famous How To Design Programs book, but I’m still at the very beginning), and even less than zero formal musical training…
So it is very possible that what you might suggest will go way over my head…
I mean, if it doesn’t work for my current level of understanding, I’ll go on on doing what I’ve been doing so far… It works, but I’ll take longer…
I can almost see what you’re getting at. I reckon have another go
It might help to provide a more detailed/concrete/specific example if you can - more visual maybe or a code (or pseudo-code) example to illustrate your question - how it reads currently, without your ‘generalist functions’, then roughly what the intended goal is. Might be easier than just trying to describe it in an abstract way…
Sounds like defining your own function, and passing
parameters to it is what you are after…
use_bpm 90
define :hats do |d|
density d do
sample :drum_cymbal_closed
sleep 1
end
end
live_loop :trapHats do
hats(ring, 4, 4, 3, 8, 4, 6, 4, 16).tick
end
n = [1, 0, 1, 0, 2, 0, 0, 0, 1, 1, 0, 1, 2, 0, 1, 0]
live_loop :trapBeat do
n.each.with_index do |i|
sample :bd_haus if i == 1
sample :drum_snare_hard if i == 2
sleep 0.25
end
end
Hey guys… Thanks for the responses…
As I said before, I’m very new to programming.
As such, I realize some of my questions might be a bit silly…
I think this was one of those silly questions… Sorry about that.
I have thought about this a little better, and I realized that I just had to turn more elements of the definitions of my functions into parameters… That would allow me to call the function as usual when I need to trigger it, and then “load” the relevant data (variations in melodies or drums) that I need at a given moment, by just changing the values for those parameters during a new call to the same function… Obvious and simple.
It seems I forgot that more things can be turned into parameters, any part of the function really…
Coming late to this party, and it would seem from the thread that you’ve solved your problem, but I’ll weigh in anyway, I hope you don’t mind.
There is an oft-quoted saying in computing: There are two hard problems in computing: cache invalidation and naming things (and buffer overruns, and so on…). It is difficult to come up with good names for functions, which are descriptive of what they do and/or how to use them.
I think it takes practice, and also you can learn a few things (good and bad) by reading other people’s code. So here are a couple of examples:
Hi! I’m not sure what you are trying to achieve and why having functions with parameters can’t achieve it, but your description “instances of functions” sounds a lot like a concept called “higher order function” which (to simplify it a bit) consists of functions that returns other functions (it can also refer to functions that take other functions as parameters, which is more or less what we do when we use blocks, but here I’ll use it to mean the former, returning a function).
In Ruby we can do that with lambda. The object returned behaves like a function, the only difference is that you can’t call it directly with whatever, whatever(), or whatever(args); you need to explictly use the method whatever.call (although there is a shortcut with whatever[args]).
All this probably sounds blurry, I’ll use an example:
define :my_rhythm do |sample1, sample2|
lambda do
at(0, 1, 2, 3, 4) { sample sample1 }
at(1, 3) { sample sample2 }
sleep 4
end
end
elec_rhythm = my_rhythm(:bd_haus, :sn_dub)
acoustic_rhythm = my_rhythm(:drum_kick, :drum_snare)
# now elec_rhythm and acoustic_rhythm are like functions, only that you need the call method
live_loop :drums do
elec_rhythm.call
end
As I say this doesn’t achieve anything that you can’t achieve passing arguments to normal functions, but maybe it helps clarify naming in some cases.
(I’m sorry I don’t have SonicPi in this computer so I could only verify that this works to some extent, using a normal Ruby; So there might be some tiny detail of sonic-pi specific code (the use of at, the sample names) that might be wrong but the principles hold).
it seems to be an at syntax question and the samples name was wrong (as you warned us). Hard to guess when you are far from sonicpi @porras
So this works
# tested on v3.3.1 04/03/2022
#
use_bpm 120
define :my_rhythm do |sample1, sample2|
lambda do
at [0, 1, 2, 3] do
sample sample1
end
at [1, 3 ] do
sample sample2
end
sleep 4
end
end
elec_rhythm = my_rhythm(:bd_haus, :sn_dub)
acoustic_rhythm = my_rhythm(:drum_bass_hard, :drum_snare_hard)
# now elec_rhythm and acoustic_rhythm are like functions, only that you need the call method
live_loop :drums do
elec_rhythm.call
end
live_loop :drums_3 do
acoustic_rhythm.call
end
Edit : it’s an interesting way of creating music
#
#
use_bpm 120
define :my_rhythm do |sample1, sample2|
lambda do
at [0, 1, 2, 2.5, 3] do
sample sample1
end
at [1, 3 ] do
sample sample2
end
at [0, 0.25, 0.5, 1, 1.5, 1.75, 2, 2.25, 2.5, 3, 3.5 ] do
sample :drum_tom_lo_hard, finish:0.75, compress: 1, cutoff:90
end
sleep 4
end
end
elec_rhythm = my_rhythm(:bd_haus, :sn_dolf)
##| sample :vinyl_backspin
s = [ :vinyl_scratch, :vinyl_rewind, :vinyl_hiss]
# as this you can see clearly which sample you are going to use with its name
# you can change the index to get the sample you want and reload to hear the changes.
acoustic_rhythm = my_rhythm( s[2], :bass_dnb_f)
# now elec_rhythm and acoustic_rhythm are like functions, only that you need the call method
live_loop :drums do
elec_rhythm.call
# you can comment this line as you want :-)
sleep 4
end
live_loop :drums_3 do
acoustic_rhythm.call
end