Hey all,
Chiming in on the various Sine-related topics here. SinOsc was designed like
most of ChucK, to do things from the definition, without particular regard for
efficiencies. In fact, a native Math.sin(BLAH) call isn’t that hideous. It used
to be in the olden days, and we avoided it like the plague. The Motorola DSP
56000 had a built in quarter-wave of sine in ROM. (It also had an 8-bit muLaw
decoding table built in as well). Many papers (Snell, Others) were written about
the optimal length, bit-depth, etc of a sine table given different types of interpolation.
Yamaha chips stored sine quads in both linear (for addition) and log (for
multiplication) ROM, or hard-coded into gates for the absolute minimum expense.
Wavetables:
Indeed, SndBuf is ChucK’s built-in wavetable (in .loop mode). And, it could be a
little cheaper, and it has built-in linear interpolation. If you want less distortion, just
make your table longer. The “table” here is a .wav file you create and load.
All you need add is an accessing function to set rate as a function of frequency.
And you could also add FM, PM, and other .sync-like functions as well. By this
time, it might be worth comparing to SinOsc efficiency for FM and other audio-
rate modulation tho. I just did a quick check and find SndBuf to be about equal
to SinOsc (the number I can run before audio starts breaking up, just over 400).
See code below.
Note also that there are some other wavetable-like UGens already in the GenX
family in ChucK. I don’t know about the relative efficiency of those, and I do
know that that you need to drive (some of) them with a Phasor. So again, it
might not end up being significantly cheap.
On unit-circle sine generation: Spencer has already given us a great ChuGen
called MagicSine, that does the rotation-based sine generation using only
a couple of multiplies and adds. It’s most efficient if you’re not changing
frequency rapidly.
Julius and I also did a “waveguide resonator” oscillator (similar to Magic
Sine), but it also had built in exponential envelope (phasor that spins outward
or inward), and allowed for modulation of the parameters for FM-like
wave distortion. Not particularly efficient if you just want a sine wave.
I have a ChuGen for that too, but never released it.
So the topic "many ways to skin a sine" is still active today!!
My comparison codes (in terminal, miniAudicle may vary, also Audio BUFSIZE might help):
/* SinOscCompare.ck ***************************************/
SinOsc s[1000];
Gain outMix => dac;
for (int i; i < 4000; i++) {
if (i%100 == 0) <<< "Sines", i+1 >>>; // this could be costing us some
s[i] => outMix;
Math.random2f(100,3000) => s[i].freq; // as would this
1.0/(i+1.0) => outMix.gain; // and this
10*ms => now; // but if those dominated, we’d probably hear 100 Hz distortion
}
vs.
/* SndBufCompare.ck ***************************************/
SndBuf s[1000];
Gain outMix => dac;
for (int i; i < 4000; i++) {
if (i%100 == 0) <<< "SndBufs", i+1 >>>; // this could be costing us some
"special:dope" => s[i].read; // maybe this too, but cacheing should help
1 => s[i].loop;
s[i] => outMix;
Math.random2f(0.2,5.0) => s[i].rate; // as would this
1.0/(i+1.0) => outMix.gain; // and this
10*ms => now; // but if those dominated, we’d probably hear 100 Hz distortion
}