for a list I can use insert method to add items into it. how to do the same thing for a ring?
Hi,
maybe this is what you are looking for:
It is part of the official tutorial so you should be able to open it from within Sonic Pi.
Martin
(You can also access the tutorial online - so the above is also at http://sonic-pi.net/tutorial#section-8-5).
Thanks! But I’ve checked out this tutorial before. What I want is something like:
r = (ring :e2)
r = r + :e3
puts r
// (ring :e2,:e3)
seems no solution in the tutorial?
This might be what you are looking for
r = [:e2] #initialize r as an array
r.push(:e3) #push new value into array
puts r # prints [:e2, :e3]
The example below just shows that the array will run as a ring. If it was an array, it wouldn’t repeat the notes in the array.
live_loop :ringDemo do
play r.ring.tick #runs through array as a ring
sleep 1
end
Hope that helps. Just be aware that push is part of Ruby, not Sonic PI, so there is always a chance it might not work at some point.
Yes! That exactly is what I am looking for ~ Thanks:)
Hi there,
it’s important to point out that Sonic Pi’s rings are immutable. This means that you can’t change them in any way - which makes them safe to share across threads and live loops
You can read about the various operations you can perform on rings here: http://sonic-pi.net/tutorial.html#section-8-5 Note that you should always use a new variable to capture the new ring, as the original ring is never changed:
a = (ring 1, 2, 3)
b = a.reverse
# a is not changed!
However, as @mrbombmusic points out, you can use normal arrays which aren’t immutable (and are therefore not sensible to share across threads) and then convert them to a ring with .ring
to give you the ring abilities.
Also, rings do support push:
a = (ring 1, 2, 3)
b = a.push 4
puts b #=> (ring 1, 2, 3, 4)
puts a #=> (ring 1, 2, 3)
Note that you have to assign the result of push to a new variable as this does not modify a
, but produces a whole new ring
Happy to answer any further questions you might have.
Hi samaaron,
I just tried out your example of push with rings and it did not work. Is this something that has changed in newer versions? This is somewhat of an old thread.
a = (ring 1, 2, 3)
b = a.push 4
This example gives the following error:
Thread death!
undefined method `push` for (ring 1, 2, 3):SonicPi::Core::RingVector
I think it has changed. However, as mentioned above lists are not immutable so you can do this. Convert a to list, push new value, convert to ring.
a = (ring 1, 2, 3)
puts (a.to_a.push 4).ring
gives (ring 1,2,3,4)
well wouldn’t it be better to say “convert to an array” than “convert to a list” no ?
to_a
converts to an array. a as array.
Then we can use a lot of functions
https://www.shortcutfoo.com/app/dojos/ruby-arrays/cheatsheet
Sorry but i don’t catch really the difference between list and array… If someone can explain, he is welcomed !
But we can be surprised to see that some array functions work on rings. See reverse that returns a ring with elements reversed.
With spi 3.2.2, we can use look and tick with arrays as we use them with rings, right ? see below.
So as said Robin, the difference you can’t add elements into rings, rings are immutable. So that’s why i can’t push an element into a ring but i can reverse it, that’s it ?
an_array = [:c, :e, :g]
puts an_array
an_array.push(:c5)
foo = an_array.reverse()
puts foo
## ringo star
puts ringo = (ring :c2, :e2, :g2, :c3)
# this works
puts ringo = ringo.reverse()
# but push no
# ringo = ringo.push(:c7)
live_loop :play_array_as_a_ring do
use_synth :piano
tick
play foo.look
sleep 0.5
end
live_loop :play_ringo do
use_synth :tb303
tick
play ringo.look
sleep 0.5
end
Maybe some clearings are needed
Cheers.
Yes sorry I’m being a bit lax about terms. I’m using list for 1 dimensional array.
The original example worked for example in SP 2.7 Since them things have been tightened up and you can’t use the example with SP 3.2.2.
Essentially you have to convert the ring to an array (if 1 dimension I have referred to this as a list). Then since this is not immutable you can push an extra value to the end and assign it to a new ring. with the .ring method.
Hi,
a somehow related issue:
A while ago I was looking for a solution to store configurations in lists (some of them 2-dimensional). Based on feedback of @robin.newman (can’t find the thread right now, where this was) I wrote the following helper function to update lists stored and retrieved via the set/get mechanism. This works quite well and might be helpful for others; here is the function and an example. Right now you can only update existing list items (which is what I need as I am changing config lists) but this can be modified to be able to also add or delete items:
# Update item in frozen array (like in: set :lst, [0, 0, 0])
#
# check which element to be replaced
# last? new value = tail
# first? new value = head
# or somewhere in between ...
# concatenate head [item] tail
# args:
# name (symbol refering to list such as :my_list or simple list)
# pos (position to be updated with ...)
# val (... new value)
define :upd_list do | lst, pos, val |
#puts "-- upd_list() --"
#puts "list: #{get(name)}"
#puts "position: #{pos} | value: #{val}"
item = [val]
if lst.is_a? Symbol
len = get(lst).size
else
len = lst.size
end
if pos + 1 == len
tail = []
else
if lst.is_a? Symbol
tail = get(lst)[pos + 1..-1]
else
tail = lst[pos + 1..-1]
end
end
if pos == 0
head = item
set lst, head + tail
elsif pos < len
if lst.is_a? Symbol
head = get(lst)[0..pos - 1]
else
head = lst[0..pos - 1]
end
set lst, head + item + tail
elsif pos >= len
puts "Error: Position #{pos.to_s} for value #{val.to_s} "
puts "is not within this list (length: #{len.to_s})."
end
end
set :a, [0, 1, 2]
puts get(:a)
upd_list(:a, 2, 4)
puts get(:a)
a = [0, 1, 2]
puts a
b = upd_list(a, 2, 4)
puts b
sick!
j = [1,2,3]
puts j
j.push(4)
j2 = j.ring
j = j2
puts j