How fast can SC do calculations? (Is sampling rate related to that?)

Hi, do you guys know how fast can SC compute values? (For instance: how fast could it iterate through an array of values.) Thanks!

You can test this. It will be different depending on cpu, os, etc. And of course the calculation you are running.

(
a = Array.fill(1000000, {20.rand});
t = Main.elapsedTime;
a.do{|item, i| item*i};
(Main.elapsedTime-t).postln;
)

There is a bench method – you don’t have to do the timing measurement by yourself.

The current SC interpreter will never perform as well as languages that resolve method calls at compile time.

There’s an effort to create a JIT compiler SC-to-native-machine-code but it’s experimental as yet. That could be faster for some types of code (though perhaps not faster to compile).

hjh

I was wondering if the sampling rate can in any way affect how fast a calculation can be performed.

In my case, that’s a variable that holds a period and and array of 127 values which must advance every 1/freq s to create a waveform with the Duty uGen:

noise = [ Dseq(1/freq), Dseq(~values) ];

But I can only generate a frequency as high as 1/sampleRate and I’m confused about it. :thinking:

A waveform’s frequency may never go higher than sample rate / 2. If you try, the frequencies will fold over back into the range 0 … sr/2.

This is not a SuperCollider limitation. It’s a mathematical limitation of all sampled functions – therefore all audio software everywhere is subject to it. See the Nyquist-Shannon sampling theorem.

Edit: You might try loading your data into a Buffer. SC has buffer readers that will perform interpolation. That might be what you need here.

hjh

It’s not that they fold over, but the shortest period I can specify with sampling rate 96000 is

(1/757) / 127 = 0.00001040160601 s

Shorter period always output the same 757 Hz frequency. :thinking: The waveform must be generated iterating through the 127 values, and the period of 1/96000 is very similar to that last period, that’s why I thought that sampling rate and “fastest calculation speed” were related?

1/96000 = 0.00001041666667

I’m afraid I’m not understanding what you’re trying to do.

Why does it have to be Duty?

hjh

I’m not sure if I could achieve it with other uGens but basically I’m “drawing” a noise signal from scratch. I have this array:

dutyCycleNoise = [ Dseq(1/freq, 1), Dseq(~finalStates7, 1) ]; 
// finalStates = 127 values representing the amplitude

Then feed it here:

duty = Duty.ar(
 		dur: Dseq([ dutyCycleNoise[0] ], repeats: inf), reset: 0, 
 		level: Dseq([ dutyCycleNoise[1] ], repeats: inf)
);

Let’s assume that the shortest duration possible in Duty is 1/sr.

Also, Dseq can only step forward one at a time. So, the minimum number of samples to a cycle is 127.

And the maximum frequency is sr/127. At 96kHz, 96000 / 127 = 755.9.

Now, it sounds like you want to preserve the 127 samples in a row, always, and have frequencies higher than 755.9.

Well… you can’t. If the frequency is higher than sr/length, then the period will be shorter than 127 samples. You’re not going to be able to squeeze 127 values into fewer than 127 samples. Dseq cannot skip values so you’re stuck here. You can compromise on the specific sample values at the output, or you can compromise on the range of possible frequencies, but you can’t get them both.

What you can do is to use an interpolating buffer reader as the oscillator, e.g. a Phasor driving a BufRd. Then the period can be anything you need. It won’t output the exact sample values but it will follow the shape of the waveform. (EDIT: But, reading through the wavetable faster effectively downsamples it – so, some frequencies that were valid at the original wavetable sampling rate will alias for the new, smaller number of samples. This is unavoidable.)

hjh

Thank you @jamshark70 !

That makes sense. :white_check_mark:

Here’s one way:

d = Signal.fill(127, { 1.0.rand2 });

s.boot;

b = Buffer.sendCollection(s, d, 1);

(
a = {
	var freq = MouseX.kr(5, 2000, 1);
	var phasor = Phasor.ar(0, freq * SampleDur.ir, 0, 1);
	(BufRd.ar(1, b, phasor * BufFrames.kr(b), interpolation: 4) * 0.1).dup
}.play;
)

(BufRd uses linear interpolation by default; cubic (4) is more accurate.)

hjh

1 Like