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
Is someone would be able to replicate those two examples using math operations ? The second one is “smoothed”.
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.
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);
)
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.)
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.
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;
)