Hey @jessicaaaaaaaa!
This one might not be as obvious at first glance, but it’s related to the permitted structure of Ruby code.
Wherever you see lists of opts separated by commas in Sonic Pi code such as attack: 0.1, release: 0.5
, Ruby (the language that Sonic Pi is based on) interprets these as a thing called a Hash, which is basically just a fancy name for a group of values that each have corresponding named labels (keys). (Here, the keys are attack
and release
, and their values are 0.1
and 0.5
respectively).
(In fact, in Ruby, hashes are often written with curly braces around them like this: {attack: 0.1, release: 0.5}
but Ruby allows people to leave the braces off in many situations and still understand it’s a hash).
As far as your particular problem goes, Sonic Pi/Ruby is getting confused because it sees what appears to be an attempt to assign a (single) value to a variable,
harmony_end = ...
… but when trying to interpret this value to assign, it instead sees kind of an ordinary list and a hash smooshed together, and that’s not possible to interpret correctly. (It’s neither a list, because it suddenly sees a hash key attack
, or solely a hash, because it didn’t start with a key for [0.5, 0.25]
).
There are a few solutions to this - you could explicitly separate the list of times [0.5, 0.25]
from the envelope opts. It would look something like this:
use_synth_defaults attack: 0.1, release: 0.5
play_pattern_timed [:d3,:f3,:a3, :d3,:f3,:a3], [0.5, 0.25]
play_pattern_timed [:d3,:db3], [0.5, 0.25]
play_pattern_timed [:c3,:g3,:bb3, :c3,:g3,:bb3], [0.5, 0.25]
(Which granted, still repeats the list of times in every call to play_pattern_timed
- though you could do):
use_synth_defaults attack: 0.1, release: 0.5
times = [0.5, 0.25]
play_pattern_timed [:d3,:f3,:a3, :d3,:f3,:a3], times
play_pattern_timed [:d3,:db3], times
play_pattern_timed [:c3,:g3,:bb3, :c3,:g3,:bb3], times
Or, you could keep the times and envelope opts together as you have above, and make Ruby understand them as distinct things. To do that, this is what it would look like:
harmony_end = [[0.5,0.25], attack: 0.1, release: 0.5]
play_pattern_timed [:d3,:f3,:a3, :d3,:f3,:a3], *harmony_end
play_pattern_timed [:d3,:db3], *harmony_end
play_pattern_timed [:c3,:g3,:bb3, :c3,:g3,:bb3], *harmony_end
In this one, a single value (a single list) is being assigned to harmony_end
, where the list consists of a smaller list for timing, and a hash for the envelope opts.
(However, to make play_pattern_timed
understand that harmony_end
is really two things, timing and a hash of envelope opts, we put an asterisk *
in front of it, which treats the code as if the items in harmony_end
were being passed as separate parameters to play_pattern_timed
, instead of just a single list of something. (It’s called the splat
operator if you are curious).
Apologies if the above was too long and detailed or technical feel free to just pick one of the suggested code solutions and run with it anyway!
I am (and I am sure others are) always happy to help with any further questions you might have