Generate those single cycle waveforms on SC

Hello,

I love a lot those specific single cycle waveforms (2048 frames) and I used them a lot, in various pulsar or wavetable applications. They come from a pack and they are called “iterative sine”.
I would love to be able to learn to design them from scratch (using Signal or so), in order to be able to produce variations around ; but it’s well beyond my basic math capacities :slight_smile:
Is someone would be able to replicate those two examples using math operations ? The second one is “smoothed”.

Thanks a lot

would be cool to have either a stateless waveshaping function or PM with control via an index for more modulation capabilites instead of loading static single cycles to a buffer, i think.

Exactly ! How is it possible ?

Wow, thanks a lot Nathan. But would it be too much to translate that into SC to fill a Buffer ? I don’t know what to do from that.
:slight_smile:

This is what I can come up with before my wife yells at me.


(

{

a = LFSaw.ar(-200, 1, 1)+

SinOsc.ar(-400, 1pi, 0.5);

a = LPF.ar(a.fold(-1,1).fold(-0.75, 0.75),3000);

}.plot

)

(

{

a = LFSaw.ar(-200, 1, 1)+

SinOsc.ar(-400, 1pi, 0.5);

a = LPF.ar(a.fold(-1,1).fold(-0.75, 0.75),3000);

a+LFNoise1.ar(10000, 0.1)

}.plot

)

Sam

LOL. Thanks a lot Sam.

Signal.sineFill might be useful.
This code adds small amounts of random harmonics to create a Signal and plays it.
Each time you run it, the posted array shows the random harmonic levels in case you hear one you like.

(
var signalSize = 2048;
var harmonicTotal = 31;
var harmonicProb = 0.25;
var minLevel = 0.05;
var maxLevel = 0.13;
a =  [1] ++ harmonicTotal.collect({arg i;
	harmonicProb.coin.binaryValue * minLevel.rrand(maxLevel);
});
b = Signal.sineFill(signalSize, a);
// plot, post & play signal
[a,b].plot;
a.post;
b.play(true, 0.2);
)
1 Like

Superb and clear little piece of code. That is exactly what I was looking for, thanks a lot.

BTW, the ddwWavetableSynth quark includes a utility to smooth wavetables by removing upper partials (so that, at higher frequencies, it isn’t exceeding Nyquist), and a player for wavetables that have been processed this way.

(
var spectrum = Array.fill(250, { 1.0.rand });
(0..249).scramble.keep(120).do { |i| spectrum[i] = 0 };
x = Signal.sineFill(2048, spectrum);

w = WavetablePrep.new.readStream(SoundFileStream(x));
)

// see it (optional)
y = WavetablePrepGui(w).front;

// pop it over to the server
z = SoundFileStream.new;
w.writeStream(z);

s.boot;

b = Buffer.sendCollection(s, z.collection, 1);

// hear it
a = { (MultiWtOsc.ar(MouseX.kr(100, 1000, 1), bufnum: b) * 0.1).dup }.play;

a.release;

In that example, it’s up to you to provide x (the Signal containing the full-spectrum wavetable). The quark just gives you an easier way to use it.

(Note: I just pushed a couple of minor bug fixes, so if you installed this quark before, better update.)

hjh

1 Like

Buffer methods sine1/sine2/sine3 might also be useful.
Here’s the previous code rewritten using sine1 with Osc.ar to play the buffer:

// prepare buffer
s = Server.local;
b = Buffer.alloc(s, 2048, 1);
)
(
// generate buffer and play
var playFreq = 220.0;
var outLevel = 0.2;
var harmonicTotal = 31;
var harmonicProb = 0.25;
var minLevel = 0.05;
var maxLevel = 0.13;
a =  [1] ++ harmonicTotal.collect({arg i;
	harmonicProb.coin.binaryValue * minLevel.rrand(maxLevel);
});
b.sine1(a);
b.updateInfo;
// plot, post & play 
// a.plot;
b.plot;
a.post;  // post the harmonic levels
{Osc.ar(b, freq: playFreq, mul: outLevel)}.play;
)

sine2 and sine3 are more flexible allowing you to set the individual frequencies and phases of the added sine waves going beyond ordinary harmonics.

1 Like

Beefing up the number of harmonics while reducing aliasing:

// prepare buffer
s = Server.local;
b = Buffer.allocConsecutive(8, s, 2048, 1);

(
// generate buffer and play
var playFreq = 220.0;
var harmonicTotal = 200;
var harmonicProb = 0.25;
var minLevel = 0.05;
var maxLevel = 0.13;
a =  [1] ++ harmonicTotal.collect({arg i;
	harmonicProb.coin.binaryValue * minLevel.rrand(maxLevel);
});

b.do { |buf, i|
	var spectrum = a.keep(a.size div: (1 << i));
	buf.sine1(spectrum);
};
a.post;  // post the harmonic levels
)

(
{ |amp = 0.1|
	var freq = MouseX.kr(50, 2000, 1);
	// considering 100 to be a baseline freq
	// where the 200 partials in the first wavetable
	// are OK
	var index = (freq / 100).log2.clip(0, b.size - 1.001);
	VOsc.ar(b[0].bufnum + index, freq: freq, mul: amp).dup
}.play;
)

hjh

2 Likes

That’s great, thanks a lot James, Paul and Sam.