Waveforms should:
-Not have phase shifts in the middle of straight lines (eg: at wrap-around
 to 0 degrees)
-Have equal amplitude in both directions when signed (eg: range of +127
 to -128 is not acceptable)
-Not be stupidly slow to access

Triangle is fairly simple to do algorithmically:
 0-1023:
   range is 0 to 126
 1024-2047:
   range is 127 to 1
 2048-3071:
   range is 0 to -126
 3072-4095:
   range is -127 to -1
This can be calculated fairly easily using  bits from the index.

Sawtooth seems awkward to do evenly. My solution was...


+7              X
+6            XX|
+5          XX  |
+4        XX    |
+3      XX      |
+2    XX        |
+1  XX          |
+0 X            ||            X
-1               |          XX
-2               |        XX
-3               |      XX
-4               |    XX
-5               |  XX
-6               |XX
-7               X

Benefits of this shape are, that the drop is *still* instantaneous (one of
my other solutions had it an extremely sharp slope), and the ramp is
perfectly straight. Supposing you accept lines terminated such as straight
:)

Disadvantage seems to be, that range of height cannot be a round number if
the wavelength is, and vice-versa. Not sure if that's solvable :(
I think perhaps as the peak magnitude is only even reached for a tiny amount
of the waveform, it would be possible to go *short* of it?
Peak is simply width/2. So... OH. To have a wavelength that is a round
number, the peak would have to be ABOVE 127 X(

AHA! This solution is... icky, but...

+3                X
+2          XXXXXX|
+1    XXXXXX      |
=0 XXX            |
   0123456789ABCDEF

The line is... *sort of* even, and it has full range. ONLY QUESTION:
Does this work the same elsewhere?

Line is made up of 3 parts:
 1 part of length N
 X parts of length 2N
 1 part of length M, between 1 and N inclusive?

So, wavelength there is (2X+1)N + M
Height is X+2, peak is X+1.
So, peak of 127 requires X=126.
Hence wavelength must be (252+1)N +M, or M+253N
Let's try for a wavelength of about 1024 for a start:
  1024=M+(253N)
M<N, so we could say in fact 1024=N X some number between 253 and 254...
1024/253 or 254 is 4 and a bit. So...
 N=4, M=a bit ;)
1024=1012+10. Ok, that's not right...
Hmm.
Ah, need integer values of N, *then* find W and M :P
N=3
W=(253*3)+M=759+M
N=5
W=(253*5)+M=1265+M
N=6
W=(253*6)
GAH....
Ok, I like this other method better:

wavelength to 2048 (so 0<=index<2048)
(255*index)/2048 gives range 0 to 254. Subtract 127, range of -127 to 127.

For 16-bit version, things get hairier because we risk bursting a long!
We use wavelength of 65536, so 0<=index<65536
(65535*index)/65536 should (if it doesn't screw up) give range 0 to 65534.
Subtract 32767 for range of -32767 to 32767.

Is all this really worth it just to make it cut as deep both ways?

For sawtooths in the range 0-127, can make it a simple right-shift (after
getting right phase) at least.
