I’ll preface this by saying that, if using Ruby classes makes the most sense to you, go for it. Even if it stops working in the future, you can make a note in your compostions of the SonicPi version that you used and run them on that.
That being said there are approaches that don’t step outside officially supported syntax.
An approach I haven’t used, could be something like this. I thinks this most closely apes the object/class way of doing things:
clear
define :createBassSynth do |n|
myNote = n
define :playBass do
use_synth :blade
play myNote
end
define :setBassNote do |n|
myNote = n
end
define :playBassChord do
use_synth :blade
play chord(myNote, :minor)
end
puts "Initialised bass synth with a note of #{myNote}"
end
createBassSynth(60)
playBass
setBassNote 48
sleep 1
playBass
sleep 1
playBassChord
Here we have several functions that share a single scope through the createBassSynth
function. Sure it’s note a single object as you were saying, but it is functionally and conceptually similar. If you want you can adjust the functions so they are called BassDotPlay
or use your own conventions so that it is clear these functions belong to the same scope.
Another solution could be to define bass information in a hash, and use functions that know how to interface with that hash. For example:
clear
bassInfo = {
mainNote: 48,
highNote: 52,
synth: :blade
}
define :useBassSynth do |info|
use_synth info[:synth]
end
define :playBass do |info|
useBassSynth(info)
play info[:mainNote]
end
define :playBassHigh do |info|
useBassSynth(info)
play info[:highNote]
end
playBass bassInfo
sleep 1
playBassHigh bassInfo
Now there are a couple of stylistic decisions you could make here. For example passing arround bassInfo
seems clunky to me. So, rather than accepting a generic info
argument, the functions could just reference bassInfo
directly. I suppose it depends on whether you want to use these functions with any other info hashes.
Another thing is whether you want to use setters or not. If you do, you could abstract away the fact that a hash is even used. For example
clear
bassInfo = {
bassNote: 48,
synth: :blade
}
define :useBassSynth do
use_synth bassInfo[:synth]
end
define :playBass do
useBassSynth
play bassInfo[:bassNote]
end
define :setBassNote do |n|
bassInfo[:bassNote] = n
end
playBass
sleep 1
setBassNote 42
playBass
It ends up looking a lot like the first example, and again you can use some kind of naming conventions so it is clear to you that these functions are all related.
This is more akin to the approach I’ve been using, hashes that define the relevant information and functions that know how to work with these hashes.