Oscillators
Oscillators in Afterglow are a flexible way of turning the timing information tracked by metronomes into waveforms that can be used to make lights do interesting things. They can be related to the beats, bars, or phrases of the metronome, or multiples or fractions thereof, and can create sawtooth, triangle, square, or sine waves, or any custom shape you can dream up and code a shape function for.
Metronomes keep track of musical time relative to some starting point, in terms of beats, bars (by default four beats to a bar), and phrases (usually eight bars to a phrase). In addition to keeping track of the current beat, bar, and phrase, metronomes can also tell you the phase of that beat, bar, or phrase, which is a measurement of progress through the beat, bar, or phrase. The phase starts out at 0.0 at the very beginning of the interval, and grows towards, but never quite reaches, 1.0, because at that point you will be on to phase 0.0 of the following interval.
So, in a sense, by itself a metronome can give you a sawtooth wave related to its intervals, just by looking at the interval phase. The sawtooth oscillators build on that by letting you change the direction of oscillation, so it starts at 1.0 and slides downward, the speed of oscillation, so it ramps over multiples or fractions of an interval, and also let you shift the phase of the wave so it does not coincide with the interval itself.
So that all of the lighting effects created for a given frame of control output are synchronized and share the same notion of the current state of the metronome, Afterglow takes a snapshot of the metronome at the start of the frame, and the oscillators work from that.
Sawtooth Oscillators
(afterglow.effects.oscillators/sawtooth)
Returns an oscillator which generates a sawtooth wave relative to the phase of the current beat. At the start of the beat, the value will be 0.0, and at the end of the beat, the value will have grown linearly to 1.0.
You can change the nature of the wave by using optional keyword parameters, and the values you use with them can be be dynamic parameters:
Parameter | Default | Purpose |
---|---|---|
|
|
When |
|
|
Change whether the oscillator cycles over each
beat, bar, or phrase, by passing in the keyword |
|
|
Runs the oscillator at the specified multiple or fraction of the interval (beat, bar, or phrase). |
|
0.0 |
Offsets the oscillator from the interval boundary by the specified amount (where 1.0 is an entire interval, and so would have no visible effect). |
As noted in the table, the direction of the wave can be reversed:
(afterglow.effects.oscillators/sawtooth :down? true)
Ratios
All of the oscillators can be modified by supplying the
:interval-ratio
keyword argument. This argument specifies a fraction
that adjusts the speed of the oscillator with respect to the interval
it is oscillating over. For example a beat-oriented oscillator
normally goes through its entire wave shape once per beat. If you
supply an :interval-ratio
of 2
, it will run half as fast, taking
two beats to go through its waveform.
An :interval-ratio
of 1/3
speeds it up so that it only takes
one-third of a beat to go through its oscillation, and will finish
three complete cycles each beat.
These can be combined, of course, so an :interval-ratio
of 2/3
would complete three cycles every two beats.
Phase Shifting
All of the oscillators can be modified by supplying a :phase
keyword
argument, which offsets them from the actual phase of the interval that
they are tracking. For example, if you supply a :phase
value of 0.5
,
the oscillator will be pushed exactly halfway out-of-phase with the
metronome interval, so that it will act as if a beat is starting halfway
through the actual beat. A positive value shifts the oscillator ahead of
the underlying interval, and a negative value delays it. Only values
between -1.0 and 1.0 make sense, since shifting multiple intervals has
no functional difference from staying within the current interval. In
other words, passing in exactly 1.0 (or 2.0, etc.) is the same as
passing in 0.0, and will have no effect on the oscillator.
As noted in the table above, to have the oscillator work with
intervals other than beats, you use the optional keyword argument
:interval
. For example to have the sawtooth spread over each bar:
(afterglow.effects.oscillators/sawtooth :interval :bar)
And to have it oscillate over each phrase:
(afterglow.effects.oscillators/sawtooth :interval :phrase)
Triangle Oscillators
(afterglow.effects.oscillators/triangle)
Returns an oscillator which generates a triangle wave relative to the phase of the current beat. At the start of the beat, the value will be 0.0, at the midpoint, the value will have grown linearly to 1.0, and at the end of the beat it will have returned to 0.0.
You can change the nature of the wave by using optional keyword parameters, and the values you use with them can be be dynamic parameters:
Parameter | Default | Purpose |
---|---|---|
|
|
Change whether the oscillator cycles over each
beat, bar, or phrase, by passing in the keyword |
|
|
Runs the oscillator at the specified multiple or fraction of the interval (beat, bar, or phrase). |
|
0.0 |
Offsets the oscillator from the beat by the specified amount |
The effects of the :interval-ratio and :phase parameters are
discussed in more depth, and illustrated with graphs, in the
documentation for the Sawtooth oscillator. You can jump to those
sections using the links in the Purpose section of the table.
|
As noted in the table above, to have the oscillator work with
intervals other than beats, you use the optional keyword argument
:interval
. For example to have the triangle spread over each bar:
(afterglow.effects.oscillators/triangle :interval :bar)
And to have it oscillate over each phrase:
(afterglow.effects.oscillators/triangle :interval :phrase)
Sine Oscillators
Just like in musical synthesis, sine waves are the smoothest-feeling waves of all, and are good for creating gentle, subtle effects which ease in and out.
(afterglow.effects.oscillators/sine)
Returns an oscillator which generates a sine wave relative to the phase of the current beat. At the start of the beat, the value will be 0.0 and beginning to rise slowly, picking up speed as it goes, and slowing down again as it approaches the midpoint. At the midpoint, the value will reach 1.0 and begin falling slowly, again picking up speed, and at the end of the beat it will have returned to 0.0.
You can change the nature of the wave by using optional keyword parameters, and the values you use with them can be be dynamic parameters:
Parameter | Default | Purpose |
---|---|---|
|
|
Change whether the oscillator cycles over each
beat, bar, or phrase, by passing in the keyword |
|
|
Runs the oscillator at the specified multiple or fraction of the interval (beat, bar, or phrase). |
|
0.0 |
Offsets the oscillator from the beat by the specified amount |
The effects of the :interval-ratio and :phase parameters are
discussed in more depth, and illustrated with graphs, in the
documentation for the Sawtooth oscillator. You can jump to those
sections using the links in the Purpose section of the table.
|
As noted in the table above, to have the oscillator work with
intervals other than beats, you use the optional keyword argument
:interval
. For example to have the sine wave spread over each bar:
(afterglow.effects.oscillators/sine :interval :bar)
And to have it oscillate over each phrase:
(afterglow.effects.oscillators/sine :interval :phrase)
Square Oscillators
Square waves are good for abrupt transitions, like strobes, or switching between different effects.
(afterglow.effects.oscillators/square)
Returns an oscillator which generates a square wave relative to the phase of the current beat. At the start of the beat, the value will be 1.0. At the midpoint, it will instantly drop to 0.0, where it will stay until the end of the beat.
You can change the nature of the wave by using optional keyword parameters, and the values you use with them can be be dynamic parameters:
Parameter | Default | Purpose |
---|---|---|
|
|
Determines the phase at which the value changes from 1.0 to 0.0, and therefore the width of the 1.0 pulse |
|
|
Change whether the oscillator cycles over each
beat, bar, or phrase, by passing in the keyword |
|
|
Runs the oscillator at the specified multiple or fraction of the interval (beat, bar, or phrase). |
|
0.0 |
Offsets the oscillator from the beat by the specified amount |
The effects of the :interval-ratio and :phase parameters are
discussed in more depth, and illustrated with graphs, in the
documentation for the Sawtooth oscillator. You can jump to those
sections using the links in the Purpose section of the table.
|
Pulse Widths
As shown in the above graph, the square oscillator normally spends
half its time in the “on” state (at the value one), and
half its time “off” (at zero). You can adjust that by
passing a value between 0.0
and 1.0
with the optional keyword
argument :width
. This tells the oscillator what fraction of the time
to be on. For example, with the value 0.8
, it is on 4/5 of the time:
(afterglow.effects.oscillators/square :width 0.8)
Alternately, using a :width
of 0.1
causes the oscillator to be on
for only one tenth of each beat:
(afterglow.effects.oscillators/square :width 0.1)
You can shift where within the beat the transitions take place using
the :phase
argument, as with all oscillators, in the manner
described above.
The :width value must be greater than 0 and less than 1 ,
or the oscillator does not oscillate at all. A value of 0 leaves it
permanently off, and a value of 1 leaves it permanently on. Values
outside that range are not accepted.
|
As noted in the table above, to have the oscillator work with
intervals other than beats, you use the optional keyword argument
:interval
. For example to have the wave spread over each bar:
(afterglow.effects.oscillators/square :interval :bar)
And to have it oscillate over each phrase:
(afterglow.effects.oscillators/square :interval :phrase)
Custom Oscillators
You can build your own oscillator with any shape waveform that you
like by defining a shape function for it, and let Afterglow do all
the hard work of hosting it within the oscillator framework by passing
that shape function to
afterglow.effects.oscillators/build-oscillator
.
All of the oscillators you have seen so far use this approach, and you
can see how simple they actually are by looking at the source of one,
for example
triangle
,
which defines a the triangle wave oscillator. (Click on the view
source
button at the bottom of the linked documentation.)
As you will see, most of the function consists of its documentation,
and its argument declaration, and those simply get passed on to
build-oscillator
, which supports the :interval
, :interval-ratio
,
and :phase
arguments you’ve seen in all the oscillator functions.
The core of triangle
is setting up its shape function to create the
triangle wave which makes it a triangle oscillator.
Shape Functions
The shape function is the first argument to build-oscillator
, and it
is simply a function which is given the current phase of the
oscillator, ranging from 0.0 to 1.0, and must return the value of the
oscillator’s wave form when it is at that phase of oscillation. In the
case of a triangle wave, it needs to ramp up from 0 to 1 during the
first half of the oscillation (as the phase grows from 0 to 0.5), then
back down to 0 during the second half. Here is how triangle
implements that:
(fn [phase]
(if (< phase 0.5)
(* phase 2.0)
(- 2.0 (* phase 2.0))))
The arguments to triangle
are then passed along to
build-oscillator
after the phase function, and the result is the
triangle oscillator behavior you can use in your shows.
Hopefully examining this example, as well as the source of the other oscillators, can inspire you to create your own interesting oscillator shapes.