Author 
Message 
Blue Hell
Site Admin
Joined: Apr 03, 2004 Posts: 21296 Location: The Netherlands, Enschede
Audio files: 167
G2 patch files: 319

Posted: Tue Jul 07, 2015 2:45 pm Post subject:
Chebyshev ... but now for triangle waves 


This started off a long time ago ... on the G2 I had discovered two 'formulas', one for doubling a sine wave's frequency and another for a triangular wave.
For the sake of the discussion let's say we have signals in a range [1,1] and that the functions discussed here will give outputs in that same range.
Then for sine we have :
Code:  O2( t) = 2 * t * t  1 
And for Tri we have
Code:  O2( t) = 2 * abs( t)  1 
The O2 notation used stands for Order two. Higher orders where the order is a power of two are easy of course, like for example :
Code:  O4( t) = O2( O2( t)) 
For the sine wave there is a nicer generalization, the Chebyshev polynomials can be used. These are recursively defined as :
Code: 
O0( t) = 1
O1( t) = t
On( t) = 2 * On1( t)  On2( t)

And this indeed gives frequency multiplication for sine waves by 2, 3, 4, ..., n
Then the question arose .. could such a generalization be made for triangular wave forms as well. We were discussing this in the chatroom, and then Scott (JovianPyx) came up with a nice pattern for the odd order cases 3 and 5. He originally wrote them down a bit different, but we found a more regular form that lent itself to extension to higher orders better.
Code: 
O3( t) = abs( abs( 3t  1)  2)  1
O5( t) = abs( abs( abs( abs( 5t  1 )  2 )  2 )  2 )  1

And this can easily be extended for higher odd orders by prepending 'abs( abs( ' and adding an extra '  2 )  2 )'
The even cases though seem to behave quite different, order 4 would just be order 2 applied to order 2, or O4 = O2( O2). but O6 = O2( O3) = O3( O2) which does not look like a pattern really.
Sofar we found
Code: 
O0( t) = 1
O1( t) = t
O2( t) = abs( 2t)  1
O3( t) = abs( abs( 3t  1)  2)  1
O4( t) = abs( 2 * ( abs( 2t)  1))  1 = abs( abs( 4t)  2)  1
O5( t) = abs( abs( abs( abs( 5t  1 )  2 )  2 )  2 )  1
O6( t) = abs( abs( 3 * ( abs( 2t)  1)  1)  2)  1
O6( t) = abs( 2 * ( abs( abs( 3t  1)  2)  1))  1
O7( t) = abs( abs( abs( abs( abs( abs( 7t  1 )  2 )  2 )  2 )  2 )  2 )  1
O8( t) = abs( abs( abs( abs( 8t)  1)  1)  1)  1
O9( t) = abs( abs( abs( abs( abs( abs( abs( abs( 9t  1 )  2 )  2 )  2 )  2 )  2 )  2 )  2 )  1
O10( t) = abs( abs( abs( abs( 5 * ( abs( 2t)  1)  1 )  2 )  2 )  2 )  1
O10( t) = abs( 2 * ( abs( abs( abs( abs( 5t  1 )  2 )  2 )  2 )  1))  1
O11( t) = abs( abs( abs( abs( abs( abs( abs( abs( abs( abs( 11t  1 )  2 )  2 )  2 )  2 )  2 )  2 )  2 )  2 )  2 )  1
O12( t) = abs( abs( 4 * ( abs( abs( 3t  1)  2)  1))  1)  1
O12( t) = abs( abs( 3 * ( abs( abs( 4t)  1)  1)  1)  2)  1
O13( t) = abs( abs( abs( abs( abs( abs( abs( abs( abs( abs( abs( abs( 11t  1 )  2 )  2 )  2 )  2 )  2 )  2 )  2 )  2 )  2 )  2 )  2 )  1

Where order zero is a bit of a speculation really, and order one seems to make sense like this.
We were not able to find a nice form for this, like the Chebyshev definition has, and apart from that the nested abs() calls are not too nice either ... but it works.
This is similar BTW to what a wave wrapper does, and I do have some code for that but found that to be too ugly to expore it any further .. but here it is:
Code: 
procedure TModWaveWrapper.DoSlowTick; // override;
var
Modulation : TSignal;
LoMirror : TSignal;
MirrorDelta : TSignal;
Amp : TSignal;
Mute : TSignal;
Signal : TSignal;
begin
Modulation := Normalize( FInputs[ i_mod] * FInputs[ i_modamt]); // Modulation amount
LoMirror := FInputs[ i_lowlevel ]  Modulation; // Get low mirror and subtract modulatio
MirrorDelta := 2.0 * ( FInputs[ i_Highlevel] + Modulation  LoMirror); // High mirror gets modulation added. calc mirror distance, times 2
Mute := SignalToMute( FInputs[ i_mute]); // Get Mute multiplier
if Abs( MirrorDelta) < 1e6 // When mirrors are too close to each other
then FOutputs[ o_out] := 0 // make an exception ...
else begin
Amp := 4.0 / MirrorDelta; // Amplitude reduction factor due to wrapping  times two
Signal := MathFloatMod( FInputs[ i_in] + LoMirror, MirrorDelta); // Lift signal by LoMirror, and take signal modulo 2 * Delta
if Signal > 0.5 * MirrorDelta // Reflect bit above Delta back
then Signal := MirrorDelta  Signal;
FOutputs[ o_out] := Normalize(( Amp * Signal  1.0) * Mute); // Bring signal into 1, 1 range and apply Mute signal
end;
end;

Anyway .. anyone fancy a nice puzzle? _________________ Jan 

Back to top



JovianPyx
Joined: Nov 20, 2007 Posts: 1323 Location: West Red Spot, Jupiter
Audio files: 165

Posted: Tue Jul 07, 2015 3:23 pm Post subject:



Nice.
The only pattern I see with the even outputs is:
Code: 
Ow = On( Om( tri_input ) )
where w = n * m and where w is even and greater than zero, where m or n or both are even and both n and m are greater than zero.

I see a possible analog design in that. _________________ FPGA, dsPIC and Fatman Synth Stuff
Time flies like a banana. Fruit flies when you're having fun. BTW, Do these genes make my ass look fat? corruptio optimi pessima


Back to top



CreatorLes
Joined: Oct 05, 2014 Posts: 67 Location: San Antonio TX USA

Posted: Tue Jul 07, 2015 3:28 pm Post subject:



you guys are hella dope wako taco!
Les 

Back to top



Blue Hell
Site Admin
Joined: Apr 03, 2004 Posts: 21296 Location: The Netherlands, Enschede
Audio files: 167
G2 patch files: 319

Posted: Tue Jul 07, 2015 4:03 pm Post subject:



Here is an order five example ... in tri wave mode (fig 1) .. on square, saw, tri and sine wave inputs ... only the tri wave is interesting for the discussion above, but due to how the triangle is calculated (it has some builtin LPF as it is being integrated from the square wave) the image is not too good.
Edit: changed the oscillators for LFOs  those have naive waveforms, making the image look better.
Edit: added the order five Chebyshev mode (fig 2) on the same input signals.
fig 1, Tri mode.
fig 2, Chebyshev mode. _________________ Jan 

Back to top



