How to oscillate a time series: wavetable ? VOsc?

Hi ! : )

I am not sure how to word this question. I think the answer lies in using VOsc and I am not sure how to do this to fit my needs… I am also quite the beginner at using SC, so please be patient with me.

Let’s say I have some time series with a certain number of values ranging from -1 to 1. This form a sound wave. How would I use this time series as input for an oscillator in SuperCollider ?

Thank you - and let me know if anything is not clear !

Hi!

There are a couple of simple answers, and also some thoughts about interpolation that you might not need to worry about right now (but you might).

If the number of points in your time series is a power of 2, you can convert the time series to wavetable format and use the Osc family:

s.boot;

// small amount of white noise
~samples = Signal.fill(16, { 1.0.rand2 });

b = Buffer.sendCollection(s, ~samples.asWavetable, 1);

a = { |bufnum|
	var freq = MouseX.kr(50, 500, 1);
	(Osc.ar(bufnum, freq) * 0.1).dup
}.play(args: [bufnum: b]);

a.free;

The Osc family:

  • Osc: Single wavetable oscillator with linear interpolation.
  • COsc: Chorusing wavetable oscillator (linear interpolation).
  • VOsc: Crossfading among a series of wavetables (linear interpolation).
  • VOsc3: 3 wavetable oscillators at different frequencies, that can crossfade across multiple wavetables (linear interpolation).
    • Note: The crossfade in VOsc and VOsc3 means that you have to have at least two wavetables – they cannot be used with a single buffer.

Now the concern:

The built-in wavetable oscillators use linear interpolation. Depending on the wavetable’s spectrum, this could introduce noise (maybe more than you expected). My example above is kind of a worst case for this, where the wavetable has a lot of high frequencies. We can contrast linear vs cubic interpolation with the same wavetable samples.

If it looks like the lower plot will sound smoother, in fact, that’s exactly how it sounds.

The built-in wavetable oscillators can’t be switched to use cubic interpolation, but you can make a cubic interpolation oscillator by running the phase yourself, and BufRd-ing a non-wavetable-format buffer. (Osc requires .asWavetable; the Phasor/BufRd approach is incompatible with .asWavetable.)

c = Buffer.sendCollection(s, ~samples, 1);

a = { |bufnum|
	var freq = MouseX.kr(50, 500, 1);
	var phase = Phasor.ar(0, freq * SampleDur.ir, 0, 1);
	var frames = BufFrames.kr(bufnum);
	(BufRd.ar(1, bufnum, phase * frames, interpolation: 4) * 0.1).dup
}.play(args: [bufnum: c]);

a.free;

Osc uses less CPU and sounds worse – in the 2000-aughts, saving CPU cycles made a big difference. Modern CPUs are fast enough that I just go directly to cubic interpolation at this point.

If your time series isn’t a power of two number of samples, then you’d have two choices: 1/ resample it to a power of two (resamp1) and use the Osc family; or 2/ use the Phasor/BufRd approach.

hjh

3 Likes

Thank you for this thorough answer with lots of examples and explanations !

I will try it out and report back with findings, if any : )

I am unstuck !

I only had to add the data input using .asFloat - some data points as an example of usage:

~samples = [0, 1, 0, -1, 0].asFloat;

Thanks again - I had spent a lot of time reading the docs trying to figure it all out - maybe this isn’t a common usecase of sound synthesis … ?

I am very happy to be able to hear it now ! :tada:

Have a great day : )

Wavetables are pretty common, and you were heading in the right direction with VOsc (which might have thrown you off because it requires at least two consecutive buffers). There are other breakpoint-style oscillators, such as the Gendy family based on Xenakis’s work.

Be aware that this will give you a breakpoint sequence 0, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 1, 0, -1, 0 (with every other 0 duplicated). This might be more interesting than dropping the duplicate though :wink:

hjh

1 Like

Off-topic: Maybe in the future an interpolation argument could be added that would allow cubic interpolation for these oscilllators?
Best,
Paul

It would be more idiomatic to add new UGens for that – even pseudo-UGens, since the behavior can be fully expressed using existing UGens. Also, if adding an argument would push mul and add backward one position in the arg list, then it becomes a backward compatibility issue of a sort that’s come up before.

But that also begs the question that “wavetable format” exists to facilitate linear interpolation, and is not useful to facilitate cubic interpolation. I’m not sure I see the point of filling a wavetable-format buffer and then 1/ reconstructing the original samples from it (since wavetable format IIRC doesn’t store the original values) and 2/ using those original samples for cubic interpolation which hasn’t been optimized by math trickery the way linear is.

hjh