# efficient triangle to sine morphing?

I'm coding a synthesis external for MaxMSP in C. It's based on Peter Blasser's bounds and bounce concept ( http://petermopar.blogspot.co.uk/2014_04_01_archive.html )- basically triangle waves with constant cursor rate (rather than frequency) and modulated bounds - eg instead of moving between -1 and 1, the wave moves between lo and hi, with lo and hi modulated.

This morning I've been thinking of adding waveshape morphing between triangle and sine (or at least sinusoidal since the moving bounds will deform the sine).

I can think of a fairly simple way to do this:

// Assuming classic triangle moving between bounds -1 to 1
// and defining position of triangle cursor @ n = pos
// shape_amt between circa 0.1 (Tri) and 1.0 (sine)
out = sin(pos * shape_amt * 0.5* pi ) / sin(shape_amt *0.5 * pi())


ie - basic waveshaping technique - scale the portion of the lookup table that's being scanned (in this case some segment of half a sine wave) to vary the shape.

For my specific case all I'll need to do from there is build in scaling from relative position between to bounds to relative position between -1 and 1.

My question really is- is this a horribly inefficient way of achieving what I'm after? I'm new to DSP coding and to coding in C so I'm sure the way that's most obvious to me isn't the best.

The obvious performance improvement that occurs to me is to create a wavetable of the sin function and look up rather than calculate those values. Obviously that way I lose resolution on the "straighter" portions of the wave, but either a high res lookup or decent interpolation feels like it ought to deal with that fairly well. Mainly I'm considering this for LFO applications so quality needn't be utterly stellar (though it'll be nice nice if it's useable into the audio range without too much fuzzy aliasing).

But beyond that, is there just a completely different approach that would be more efficient? And if this is the best approach, are there any things I should think of when pursuing it?

Edit - Thanks for great answers below. I've ended up doing something fairly simple (and possibly crude) for the moment, which suits my purposes pretty well. I'll definitely be investigating both suggestions as improvements to this.

#define sign(a) ( ( (a) < 0 )  ?  -1   : ( (a) > 0 ) )
#define LKTBL_LNGTH 2048

void setup_lktables (t_object* x)
{   // create lookup for 1/4 sine
int i;
for(i=0, i< LKTBL_LNGTH, i++){
x->sin[i] = sin(pi * i * 0.5 / LKTBL_LNGTH) ;
}
}

double lookup_shape (t_double pos, t_double shape, t_double lo, t_double hi)
// acting on triangle wave (phase + (2*gradient*f/sr) per sample, wraps & changes dir  at <lo and >hi)
// shape comes in as restricted to (-1...-0.05, 0.05 ...1), defines the portion of lookup to use

{
t_double midpoint, halfwidth, ph, fracph, shaped;
t_int maxph, intph, sign;
// get relative position between bounds for waveshaping lookup
midpoint = lo + 0.5f * (hi - lo);
halfwidth = midpoint - lo;
// prepare phase values for lookups
maxph = shape * LKTBL_LNGTH - 1;
ph = (pos - midpoint) * maxph /  halfwidth;
sign = sign(ph);
ph = ph * sign;
intph = (int)ph;
fracph = ph - intph;
// lookup, scale & lerp
shaped = sign * (x->sin[intph] * (1.f - fracph) + x->sin[intph+1] * fracph) / x->sin[maxph];

//now return  waveshaping output scaled to actual bounds
return midpoint + shaped * halfwidth
}

• in a previous life, i implemented a very nice sinusoid with a triangle wave (first generate sawtooth, then apply absolute value, then subtract DC portion) and ran that through a 5th-order polynomial with odd-symmetry that had coefficients optimized to get you a sine. no lookup table. (and if a lookup table is needed, why not just do it with wavetable synthesis?) the 3rd-harmonic and 5th-harmonic were down 70 dB, as i recall, and the others were lower. i do not have the coefficients nor the MATLAB source i wrote to get them. – robert bristow-johnson Apr 14 '15 at 18:48
• This being such a wild question (apologies, DanBennett) I dare to use the opportunity to reply to your kind greeting Robert! It is a very nice surprise to see you here. And there are some other comp.dsp blokes too! This site is similarly addictive but unfortunately without the social chatter. – Olli Niemitalo Apr 14 '15 at 19:06
• yeah, @OlliNiemitalo, they seem to discourage that. and on some other SE forums, there is some enforcement of political correctness. it's a small world in DSP and smaller still for audio/music related DSP. – robert bristow-johnson Apr 15 '15 at 3:47
• No apologies needed, and thanks both very much for your replies. I'm not sure whether "wild" means "interesting", "completely ridiculous" or somewhere between but I'm pretty comfortable sitting anywhere on that spectrum :) I've added the solution I've ended up with above - which I'm happy with for my purposes for the moment, but both these answers give me avenues to explore for improvements. @OlliNiemitalo - I used your pink elephant paper last month in my very first audio coding attempt, (just a henon map sonification) so thanks for that too! – DanBennett Apr 15 '15 at 8:01

This answer is about using sines instead of triangles in the bounds and bounce concept. There is no way to do it so that these defining conditions of the original concept are all satisfied:

1. While the boundaries stay constant the output is a pure sine wave (triangle for the original algorithm) that goes from the upper boundary to the lower boundary.
2. Moving the boundaries suddenly does not create a discontinuity in the output.
3. Moving the boundaries suddenly does not create a discontinuity in the derivative (slope) of the output.

Say the sine wave has reached the upper boundary and the boundaries are updated. Sine waves of other amplitudes cannot have zero derivative at that amplitude value, so the scheme fails.

But you can make it work if you constrain the boundary changes to times of zero crossings of the sine wave:

You just have to decide on the slope at zero crossing and your sine turns out as: $$\text{boundary}\times\sin\left(\frac{\text{slope}\times\text{time}}{\text{boundary}}\right).$$

• Not at all what I was looking for! But chosen as the answer since it's a really interesting different avenue to pursue on the same concept, and it sticks to my constraints far more than I have. Thanks very much. – DanBennett Apr 15 '15 at 8:14