Simple Waveforms

This tutorial discusses elementary waveform generation in Kronos. They are not bandlimited, and therefore are better suited for slow moving control signals than audible signals. If you use non-bandlimited oscillators as sound sources, you will encounter a lot of non-harmonic alias distortion.


A phasor is the basic building block to many geometrical waveforms. It is a frequency integrator, producing a steadily increasing phase angle from a frequency parameter. Most practical phasors are periodic, meaning that the output waveform wraps around once past certain point. Between wrap points, the phasor output is a linear ramp. For our purposes, we will define a phasor that ramps from 0 to 1 at the audio rate, with a period determined by the frequency parameter.

Phasor(freq) { ; compute the increment per sample by normalizing frequency to 0 - Nyquist/2 ; inject audio clock with 'Audio:Signal' inc = Audio:Signal( freq / Audio:Rate( ) ) ; compute integration by recursive unit delay (z-1). The feedback loop is computed ; by adding 'inc' to the previous value 'state', and wrapping back at 1. state = z-1( Fraction(state + inc) ) ; function return value Phasor = state } snd = Phasor(220) * 0.1

Sawtooth Wave

It is pretty trivial to generate a naive sawtooth wave from a phasor. In this example, we generate a downward ramping sawtooth wave:

Saw(freq) { Saw = #1 - Phasor(freq) * #2 } snd = Saw(220) * 0.1

Note the use of invariant constants, like #2. This is a special data type in Kronos that can be implicitly converted to any data type. In practice, this makes the sawtooth oscillator capable of operating with either 32-bit single precision floats or 64-bit double precision floats.

Triangle Wave

Triangle wave is easiest to derive from the sawtooth. By taking the absolute value of the sawtooth wave, we obtain a unipolar triangle wave, which can then be rescaled to bipolar.

Tri(freq) { Tri = #2 * Abs(Saw(freq)) - #1 } snd = Tri(220) * 0.1

Pulse Wave

Pulse wave with adjustable with can be generated from a phasor via a comparator. We use boolean logic to generate either 0 or 1 based on whether the phasor exceeds a treshold. This binary value is then rescaled to -1 and 1.

Pulse(freq width) { Pulse = #2 * ( (Phasor(freq) < width) & 1 ) - #1 } snd = Pulse(220 0.5 + 0.5 * Tri(0.1)) * 0.1