Variable as Array index inside Routine

Hi all,

Noob question:
I want to extract certain values from an Array to create a new Array. The indices I want to pull from the original Array are irregularly spaced and are determined by the values at the indices after the ones I pull.
I am stuck on how to iterate the variable for the next desired index. As written below, timeindex is redefined as 0 each time the Routine iterates and I am never able to advance to other indices. If I make timeindex an arg, or assign it before the Routine, I get an error saying SC doesn’t understand the “*” operator, presumably because it can’t make sense of ~partials[timeindex + 1].

Thanks in advance!


(

		~partials.size.do({|i|
			var timeindex = 0;
			~times.put(i, ~partials[timeindex]);
		timeindex = timeindex + (~partials[timeindex + 1] * 3 + 2);
};
);
)```

Hi!
In general, it seems like you want to reorder ~partials values (since ~times is the same size of ~partials, and contains only elements from ~partials). Here are a few things I see in your code:

  1. timeindex is initialized inside your “do” block. This means that, at every iteration, timeindex is always reset to 0 (as you say). You might want to move it outside of the block, so that it can correctly accumulate values from the routine.
(
var timeindex = 0;
~partials.size.do({|i|
    ~times.put(i, ~partials[timeindex]);
    timeindex = timeindex + (~partials[timeindex + 1] * 3 + 2);
});
)
  1. There’s no guarantee that timeindex is a valid index for ~times. I don’t know what kind of data you have in ~partials, but you should think about what to do when you try to pull a value that doesn’t exist. For indices that are greater (or equal) to the array’s size, ~partials[timeindex + 1] resolves to nil, which throws the error when you try to multiply it.
    SC has some options to wrap around an array as if it was circular, or to fold (i.e. bouncing back at the extremes) and clip. You might be interested in checking out these methods.

  2. Maybe not useful for this case, but if you can pre-calculate your indices, you can pull them out all the same time:

~partials[ [1,3,5,7] ] // pulls values at indices 1,3,5 and 7

Thanks, @elgiano!

I did try moving timeindex outside of the do block, as shown below, but this returns the following error: “Message ‘*’ not understood.” I’m having trouble understanding why this error occurs, and what to do about it.
~partials contains floats and integers from a SPEAR file detailing frequency and amplitude data of partials from an audio file. I’m trying to create an array containing only the timestamps from ~partials, which are positioned between the freq & amp data. (The number of partials varies throughout the file, so the timestamps are not evenly distributed.) The ~times array will actually be smaller than ~partials, so yes, I’ll have to deal with nil values and filter them out at some point, but haven’t gotten to that yet. Thanks again!

(
	var timeindex = 0;
		~partials.size.do({|i|
		~times.put(i, ~partials[timeindex]);
		timeindex = timeindex + (~partials[timeindex + 1] * 3 + 2);
};
);
)

Ok! Now I see :slight_smile: Do you need to extract every second value of subsequent triplets?

// 0. let's pretend ~partials contains [1, 2, 3, 4, 5, 6, 7, 8, 9]

// 1. group the arrays in subgroups of 3 elements each
f = ~partials.clump(3); // -> [[1,2,3], [4,5,6], [7,8,9]]

// 2. flop it, so that you get three arrays: one for the first items of each triplet, one for the second and one for the third
f = f.flop; // -> [[1,4,7],[2,5,8],[3,6,9]]

// 3. you want that [2,5,8]. That's:
f[1] // -> [2,5,8]

// all in one line
~times = ~partials.clump(3).flop[1] // -> [2,5,8]

Another approach would be to select only the elements whose index divided by three has a remainder of 1 (that is, every second element in subsequent triplets):

~times = ~partials.select {|v,i| i % 3 == 1 }

The advantage of using .flop is for when you want to de-interlace ~partials, and get all the three sub-arrays at the same time:

(
var freqs, times, amps;
# freqs, times, amps = ~partials.clump(3).flop;
// now freqs is [1,4,7], times is [2,5,8], and amps is [3,6,9]
// note that times is not ~times. AFAIK, you can't use the # trick with environment variables
)

If only it were that simple! In my effort to strike a balance between concision and clarity, I may have erred on the side of providing too little information. The issue is that the values I’m trying to extract are not evenly spaced. The Array I’m trying to pull from is formatted as follows:
[\timestamp0, \numberOfPartials, \partialIndex0, \partial0Freq, \partial0amp, \partialIndex1, \partial1freq, \partial1amp…(values for partials 2…n)…\timestamp1…etc]
As I mentioned, the number of partials varies throughout the Array, therefore I need to be able to plug in some variable iteratively so that the Routine knows the location of the next timestamp. The intervals between timestamps will always be some multiple of 3 (because each partial has an index, freq value, and amp value), plus 1 (to account for the \numberOfPartials integer), plus 1 (to advance to the next timestamp).
So, my real question is: Why am I getting the “Message ‘*’ not understood” error when I move the timeindex variable outside of the do loop, and what can I do about it?

Hi Boris,

“Message ‘*’ not understood”

Please provide the whole error stack, or better yet, a complete reproducer. Without either of those, people are just poking in the dark, whistling up the wrong trees, bending over backwards to help you in all the wrong ways and whatnot …

Cheers,
eddi
https://soundcloud.com/all-n4tural
https://alln4tural.bandcamp.com

To answer your question directly:
You get that error because you do nil * 3, and nil, which is the receiver of Message ‘*’, doesn’t know how to be multiplied (in other words, you can’t multiply nil by anything, or you get that error).
You do nil * 3 because at some point ~partials[timeindex + 1] is out of bounds, and that gives nil. Example:

a = [1,2,3];
a[3]; // -> nil

This happens when timeindex >= ~partials.size. So, a quick fix would check if ~partials[timeindex + 1].notNil, or if timeindex + 1 < ~partials.size:

(
var timeindex = 0;
~partials.size.do({|i|
	~times.put(i, ~partials[timeindex]);
        if( (timeindex + 1) < ~partials.size) { 
	     timeindex = timeindex + (~partials[timeindex + 1] * 3 + 2);
        }
};
);
)

And then you should figure out what to do when timeindex + 1 >= ~partials.size, in my example we just ignore it and continue with the next iteration. And the next iteration will give the same result, because timeindex didn’t change…

Maybe you would like to exchange some ideas about how to parse your data? I think there is another problem in ~partials.size.do: you don’t need to run this algorithm for all elements of ~partials. Once you know how many you need to skip, you can skip them, using a while loop:

var timeindex = 0;
while {timeindex < ~partials.size} {
	var numPartials;
	~times = ~times.add(~partials[timeindex]); 
	// advance to read numPartials
	timeindex = timeindex + 1;
	numPartials = ~partials[timeindex];
	if(numPartials.notNil) { 
		// skip the numPartials slot, and numPartials partials
		timeindex = timeindex + 1 + (numPartials*3);
	};
}
2 Likes

Ah, many thanks, @elgiano! This last bit with the while loop is just what I was looking for. I had naively assumed that SC would just add nils to the Array for all of the instances of nil * 3, and that I could just filter those out later; very helpful to understand that that was a fatal flaw.
And thank you @alln4tural for the advice re: posting error stack/reproducer.

How about adding a * method to the Nil class:

*{|x| ^nil}

?

In general, it’s better to report an error at the point of failure, rather than later.

If you have a complicated function that fails because x was nil in x * 3, currently the * fails and gives you information about what operator failed, and what the operands were (and the previous stack frames tell you the context where the math op failed).

If the * instead returned nil, then very likely something depending on that result would fail later – only then, the information that it was a faulty * operation is gone. It won’t be reported directly. So it becomes harder to troubleshoot.

It isn’t actually more user friendly to permit more types of errors.

Another example from my live coding system: If I give a wrong argument value to a generator function, and I catch it and throw an error during object initialization, the system is “getting in the way” of creating the object, but the process keeps making sound according to the old expression – better for the audience. If I allowed the object to be created, then it would die later while playing – suddenly a musical element would go silent, and instead of interpreting an error like “\ins(): numToInsert should be a number” I would have to figure out some inscrutable call stack involving deep internals that should never come up in performance. Early and clear error reporting is usually better.

Btw another way to handle the final nil here would be to use doAdjacentPairs instead of do.

hjh

1 Like