Question on generating Triangle and Square waveforms

I’ve been working through Introduction to DSP Tutorial and was trying to figure out how to do the exercise for creating a triangle, square and noise waveform.

In the tutorial it goes through mapping a sine wave into a sawtooth, which is not too difficult as its just two discrete points. However, I assume you would need at least 3 discrete points for a triangle, but the jmap function doesn’t work with that.

Is there any documentation I can look into to figure out how to make those waveforms? I don’t need the answers, just some resources so I can figure out how to do it. Thanks!

Any function would do. For a triangle you can use a piecewise defined function:

osc.initialise ([] (Type x)
{
    return (x < 0) ?
        juce::jmap (x,
                    Type (-juce::MathConstants<double>::pi),
                    Type (0),
                    Type (-1),
                    Type (1));
        }, 2) :
        juce::jmap (x,
                    Type (0),
                    Type (juce::MathConstants<double>::pi),
                    Type (1),
                    Type (-1));
        }, 2);

I see, thank you! This makes a lot of sense. One question though, why do you have that (x < 0) ? operator?

Why would you need to break it down into an if else? Thanks

Because the triangle consists of a rising slope and a falling slope.
The saw tooth just ends at the point of the discontinuity.

I am sure there are many other ways to write it. I just wanted to stick with the way your original example worked.

Does that mean x in this context is the slope? Sorry if this question is obvious, I am very new to DSP. Thank you!

x is the input to the generator function aka phase, which is going from -pi to +pi and repeating.

BTW. a square is even simpler:

osc.initialise ([] (Type x)
{
    return (x < 0) ? -1 : 1;
}

there are lots of cool alternative ways to come up with waveforms for wavetables and stuff.
one tool that you should be using for that is desmos. there you can try a lot of stuff, even conditionals, so that you can immediatly visualize your ideas without testing it in the code already. for example here i made an analog sawtooth wave from a sum of sine waves:

another way to make waveforms is to read up on their wikipedia pages and choose one of the dozen of functions there to generate the waveform. for example this triangle wave wikipedia article. cool thing about that is you can add more parameters to those functions and usually find some interesting twists that can especially become useful when you make multidimensional wavetables:

1 Like

This is really good info. Thanks!

Is there a typo somewhere in your piecewise defined function? I spent ages fiddling about with it before it would compile. I managed to get this to work.

osc.initialise([](Type x)
    {
        return (x < 0) ?
            juce::jmap(x,
                Type(-juce::MathConstants<double>::pi),
                Type(0),
                Type(-1),
                Type(1)) :
            juce::jmap(x,
                Type(0),
                Type(juce::MathConstants<double>::pi),
                Type(1),
                Type(-1));
    }, 128);

Yep, that looks much better. Don’t know where I had my head above… Sorry

1 Like

Yep - building the waveshape from harmonics lets you generate tables with progressively less harmonics to avoid aliasing. One easy to read reference on this are the tutorials from Nigel Redmon: https://www.earlevel.com/main/category/digital-audio/oscillators/wavetable-oscillators/