On SinOsc, Wavetables, Unit Circles, etc.
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 }
Thanks everyone for chiming in on this interesting topic! I had suspected what Perry said: that ChucK wasn't designed with "particular regard for efficiencies." But I had no idea that Math.sin isn't as expensive as it used to be. I had always taken it as gospel that trig calls were to be avoided! Thanks for the info about resources on optimal table size, depth, etc. It sounds to me like SinOsc isn't as inefficient as I had thought, but I'm still interested in the idea of a customizable Wavetable instrument. I hadn't thought of SndBuf, but I guess with a few new methods of pulling in the table and maybe a different interpolation method (bearing in mind Nat's warning about efficiency!) I think it could be useful. Joel On 09/18/2015 12:56 PM, Perry Cook wrote:
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 }
_______________________________________________ chuck-dev mailing list chuck-dev@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-dev
participants (2)
-
Joel Matthys
-
Perry Cook