Hello,
I am a new user of sonic Pi and I’d like to use one of the examples functions: ocean inside the function choose. I have an error with the argument value.
not sure about how to call :ocean (my wild guess is that this could be done using lambda). As far as I see calling :foo won’t work this way because it already includes a call to play.
So that wasn’t really an answer to your question, was it ?
An easier way could be to include the random note generation of :ocean into your loop.
Hi Carrié
welcome to Sonic Pi.
The problem is that the play command expects a numeric note or a note symbol.
When it gets a choice of either :foo or :ocean it tries to convert it to a note symbol and fails, producing the error message that you get, as the two functions are not notes. A solution I came up with is shown below.
define :foo do
play 30
sleep 1
play 55
sleep 0.5
end
define :ocean do |num =3,amp_mul=0.125|
num.times do
s = synth [:bnoise, :cnoise, :gnoise].choose, amp: rrand(0.4, 1.5) * amp_mul
control s, pan: rrand(-1,1),cutoff: rrand(60,110)
sleep rrand(0.5,4)
end
end
loop do
choice=rrand_i(1,4) #get a random integer 1-4
puts choice
case choice # use Ruby case statement to process
when 3 #choice is 3
foo ##do foo
when 4 #choice is 4
ocean # do ocean
else # otherwise it will be a note
play [60,65,72][choice-1] #use choice-1 as an index into note list (index starts from 0 not 1)
end #end the case statement
sleep 1
end
This chooses a random number in the range 1 to 4 then uses the Ruby case statement to decide its course of action.
It deals with the two functions first. If choice is 3 it calls the function :foo
If choice if 4 it calls the function :ocean
otherwise it uses the value in choice to index which note to play from the list
Two further points. I notice you are using Sonic Pi 2.11.1 which is quite an old version. Not sure what computer you are using, but there are probably later versions available for it.
Secondly you may find it easier to copy and post your code directly into in_thread putting a line of three back ticks before it and a line of three back ticks after it as I have done in this post.
Hi @nathalierun, and welcome
On the highlighted line in that screenshot, you’ll notice that choice is 4 (which is intended to be a comment) is actually being interpreted as some kind of command to run. Change it by putting a missing # in, so that it says when 4 #choice is 4 and it should run happily
Re using a function call as a parameter to another function: it always depends on what type of thing the inner function (ie foo or ocean in this case) returns, and what the function you’re passing it into as a parameter (here, ultimately play) expects there. In your original example, you can absolutely pass a function into choose, but in that case, if choose picks foo or ocean, they do not return a number or note symbol, and play can’t handle it, as Robin says.
Hopefully with the when 4 line focused in your screenshot above fixed, Robin’s example works fine!
It’s a rather contrived example, but a way to use lambdas to do roughly what you were after in your original post might be the following:
define :f1 do |note|
play note
sleep 1
end
define :f2 do |note|
play note
sleep 0.5
end
live_loop :test1 do
function_in_a_lambda = [-> { f2(:c5) }, -> { f1(:a3) } ].choose
function_in_a_lambda.call
##| or another use of lambdas, to easily vary their parameters:
##| function_in_a_lambda = [-> (x) { f2(x) }, -> (x) { f1(x) }].choose
##| function_in_a_lambda.call([:c5, :a3].choose)
end
(A reminder that things such as lambdas come from the Ruby language directly - they are not true Sonic Pi commands, so while they might work at the moment, they’re not officially supported, and they might stop working if Sonic Pi changes enough).
Hopefully that gives you an idea. Does this example get close to what you’re thinking about?
Sure. In this particular case, there’s no way of knowing beforehand whether the parameters that are chosen match the lambda that they are passed to. We’d probably need to do one of several things: either manually identify somehow the selected lambda after you choose one, to determine what kind of parameters to send it: eg:
define :f1 do |note, note2|
play note
sleep 1
play note2
sleep 1
end
define :f2 do |note|
play note
sleep 0.5
end
live_loop :test1 do
lambdas = [-> (x) { f2(x) }, -> (x, y) { f1(x, y) } ]
selected = rand_i(lambdas.length)
if selected == 0
lambdas[selected].call(:c5)
elsif selected == 1
lambdas[selected].call(:a3, :c5)
end
end
Or, just avoid separating the parameter values outside of the lambda definitions altogether and either send them exactly when the lambdas are defined: eg [-> { f2(:c5) }, -> { f1(:a3, :c5) } ].choose, or leave the determination of those values until you are actually inside the functions f1, f2 themselves…
Ah. That one is because there are 3 items in f_lambdas, so selected can be either 0, 1, or 2. In which case, the code that decides what to do with it ends up skipping over the lambdas for 0 and 1, and because there is nothing to handle 2, the code immediately tries to loop back around and start again, but the thread has not had a chance to ‘sleep’, meaning that you get the zero time loop error. So in this case, it’s close - you just need to make that last condition selected == 2, and put in something extra to also handle the middle lambda.
No need to apologise! You are welcome. Just keep in mind that like I mentioned earlier, lambdas are not officially supported Sonic Pi commands, so may not always work going into the future.
Even so - looking forward to seeing/hearing what you create, if you’re happy to share later
Of course !
About this, can I take some code bits provided in the examples for my fractal tree music?
What about the license? Is the examples code distributed under a creative commons license? This would allow me to use parts of it.
And if I quote the authors of the pieces of code, is that enough? For instance, :ocean was coded by Sam Aaron, it’s a part of Sonic Dreams.
The music is intended for a scientific video explaining fractals trees for a competition of the Computer Society of France. If the video is selected, it will be licensed under Creative Commons.
I’ll leave the exact confirmation to @samaaron perhaps - but as far as I know, according to https://github.com/samaaron/sonic-pi/blob/master/LICENSE.md#docs-tutorial-and-examples the examples are CC-BY-SA (details of which are linked there) so as long as you abide by the relevant terms, that sounds to me like it’s fine. I’m sure one of us would be able to clarify things if you had any further questions
define :abc do
print 'abc'
play 80
end
define :doPlay do |d|
if d.is_a? Integer
play d
else
d
end
end
loop do
doPlay [abc, 80, 90, 50, 60].choose
sleep 0.5
end
Excuse me for the code, I’ve started studying it yesterday.