Discrepency in Signal concatenation when using variables

Hi,
Newbie here… I’m experiencing a bizarre behavior:

I create an Array using Signal: (0, 1, 2, 3, 4) and attach it to variable “signalarray”.
If i post or plot variable “signalarray” I get:
[0, 1, 2, 3, 4].

(var signalarray, why;
signalarray = Signal.series(5, 0, 1);
signalarray.postln;
) // outputs: Signal[ 0.0, 1.0, 2.0, 3.0, 4.0 ]

Reverse this array and attaching it to variable “why” and then plotting the unmodified variable “signalarray” i get:
[4, 3, 2, 1, 0]. (Bizarre… i haven’t changed “signalarray” only “why”).

(var signalarray, why;
signalarray = Signal.series(5, 0, 1);
why = (signalarray).reverse;
signalarray.postln;
) // outputs: Signal[ 4.0, 3.0, 2.0, 1.0, 0.0 ]

If I exchange (signalarray).reverse with (signalarray+0.0).reverse then i get what i expect:
[0, 1, 2, 3, 4].

(var signalarray, why;
signalarray = Signal.series(5, 0, 1);
why = (signalarray+0.0).reverse;
signalarray.postln;
) // outputs: Signal[ 0.0, 1.0, 2.0, 3.0, 4.0 ]
var first = Signal.series(5, 0, 1.0/50);
var second = first.copy.reverse;
(first ++ second).plot(discrete: true);

You need a copy.
reverse for some reason does this in place, it mutates the original. I don’t know why because Array doesn’t and it isn’t in keeping with how most supercollider things work. It looks like internally this is done with a primitive, so this was probably done for speed, though it is very confusing.

Thanks a lot Jordan.

I’ve actually modified my post to make it even clearer. What you suggest makes some sense to me though i don’t understand then why adding +0.0 to the Signal array before reversing it prevents this behavior. I’ve set out the three examples above.

The code at the end, evaluates to this.

(
var signalarray = Signal.series(5, 0, 1);
var temp = signalarray + 0.0; // here a new signal is made
var why = temp.reverse;
signalarray
) 
// outputs: Signal[ 0.0, 1.0, 2.0, 3.0, 4.0 ]

whereas, when you don’t add 0…

Signal.series(5, 0, 1).reverse;

there is no copy and a single signal object is mutated.

I think this is pretty dumb, and what you expect to happen is the correct thing to expect. Somewhere along the line, someone decided the performance benefit was worth the confusion, a mistake, in my opinion. Supercollider isn’t very friendly in this regard, although difficulties with figuring out where copies occur is not an uncommon issue with dynamic languages.

1 Like

Thanks @jordan! That’s really helpful. Agree with your sentiments and good to know my frustration was not misplaced.

I also ran into some… interesting decisions in the Signal API recently. It’s a subclass of ArrayedCollection but certain methods behave completely differently from superclasses. reverse is one, normalize is another (arguments are different and it normalizes peak absolute value instead of range). Also, Signal.newFrom([1, 2, 3]).linlin(0, 3, 0, 3) returns an ordinary Array instead of a Signal. :person_shrugging:

1 Like