Skip to content

Tutorial: Tempo and Sound

Mark Slee edited this page May 26, 2022 · 5 revisions

Overview

LX offers a number of ways to create time-synced effects based upon a musical information. This can be done either via custom code, by using LX's parameter and trigger modulation systems, or by responding to incoming audio data.

Synchronizing to Tempo

Tempo can be thought of as the rate of regular divisions underlying a piece of music. A tempo is analogous to a metronome because the beats it represents will be steady even if a live musician (or live audio feed) deviates from it temporarily.

Within LX, the global tempo is represented by the Tempo object. This is accessible to pattern and effect code via lx.engine.tempo. Its clock can be controlled internally, synced to an external Midi clock, or synced via OSC messages. The internal clock source can be set either by direct numeric value, or manually tapped and adjusted.

Setting the Clock Source

The Tempo clock source can be set via the global Tempo control in the top bar.

From left to right the controls are:

  • tempo.clockSource - how clock is generated

    • Int - internal clock, generated based upon fixed BPM value

    • Midi - clock will be derived from incoming Midi data for any device with Sync activated (using the metronome icon in the MIDI INPUT section)

    • Osc - clock will be derived from incoming OSC messages

      • /lx/tempo/beat - OSC message that triggers a beat, with an optional integer argument for the human-indexed beat-count
      • /lx/tempo/setBPM - OSC message that takes a floating point argument with a BPM
  • TAP - tempo.tap - learns the tempo by timing the gaps between successive taps, the tempo will be updated after 4 or more taps in a row, and the beatCount is reset to 0 on each tap. The counter resets after 2 seconds of no taps

  • 120.00 - tempo.bpm - raw data input field to specify BPM manually

  • </> - tempo.nudgeDown/tempo.nudgeUp - hold down these buttons to temporarily shift the BPM down or up in order to adjust alignment without changing the BPM itself

  • tempo.enabled - clicking turns on or off tempo trigger messages, which are fired by the BooleanParameter tempo.trigger on every beat, may be used as a trigger modulation source

The Tempo Object

The lx.engine.tempo object holds:

  • tempo.bpm and tempo.period

    The frequency (in Beats Per Minute, AKA “BPM”) and a corresponding period, typically measured in milliseconds per beat.

  • tempo.basis()

    The fraction (percentage) we are into the current beat as a double in [0→1]. This is called the basis. If we're halfway between beats, basis() returns 0.5. Think of it like a progress bar. The resulting 0→1 ramp is a useful field to consume as the primary way to sync patterns to beats. Use it as you would a SawLFO modulator with outputs from zero to one every beat. For instance, if you want a dot to move from left to right once per beat, set its fractional position directly to this value.

    You can also retrieve a sawtooth ramp for longer periods, such as a four beat measure. Since four beats is called a whole note, this is retrieved with: lx.engine.tempo.getBasis(Tempo.Division.WHOLE) - getBasis() may be called with any Tempo.Division argument.

  • tempo.beatCount()

    Returns an integer with the number of beats that have elapsed since the metronome was last triggered. This is useful for patterns that want to perform some evolving action, based upon elapsed musical time. Note the 0-indexing of the current beat number since it was reset. In music we typically count "1-2-3-4, 1-2-3-4"; but beatCount() counts incrementally "0-1-2-3-4-5-6-7-etc." from 0 without looping.

    The beatCount() is zeroed on external clock trigger events, as well as when you tap manually in LX Studio to set the internal tempo's BPM. This is why tapping out 5 beats is suggested (count "1-2-3-4-1" in your mind). It aligns your patterns to the beat and measure. If the rate seems off, wait at least 2 seconds and try tapping again. If it's only slightly off, you can nudge it into phase similar to how a DJ sometimes physically nudges rotating records. DJs learn that sensing a frequent need for a nudge forward is a hint that the rotation speed (tempo) is too slow.

  • tempo.getCompositeBasis()

    This useful quantity combines the beatCount() and basis(). For example, assuming a typical 4 beat measure, when getCompositeBasis() is 7.5, we are halfway through the last quarter note of the second measure.

  • Methods to signal that the current frame we’re composing (in a Pattern's run()) is the one that is immediately following a beat or measure (so effectively, it's on-beat).

    • tempo.beat()

      Returns a boolean that will be true if the metronome just clicked on a new beat in this iteration of the run-loop. This is a useful way for patterns to trigger some action on the start of every beat.

    • tempo.measure()

      Similar to the beat() method, but returns the boolean value of true only once every measure has completed, based upon the number of beats per measure stored in the tempo.beatsPerMeasure parameter.

    Alternatively, you can register tempo listeners that implement onBeat(tempo, beatInMeasure) or onMeasure(tempo).

    This is useful for triggering something that animates at a speed independent of tempo. If you're new to writing animations, first try using the basis(), fractional values between zero and one.

Pattern code may query any of the above methods in combination to perform custom time-based calculations.

Using Synced Modulators

Many of the core LX Modulators extend from the LXPeriodicModulator which offers tempo synchronization options. Rather than passing a fixed period, these modulators may be configured to automatically follow tempo divisions. Among others, this applies to SawLFO, SinLFO, TriangleLFO, SquareLFO, and Click.

All LXPeriodicModulator instances offer the following parameters:

  • tempoSync - Whether this modulator syncs to a tempo
  • tempoDivision - Enum value of type Tempo.Division, which specifies the musical time-value
  • tempoLock - Whether the modulator is locked in phase alignment with the absolute tempo clock, or allowed to start/stop freely
  • period - A manually specified period, only used when tempoSync is off

An LXPeriodicModulator behaves as follows under their various tempo-aware settings:

  • If tempoSync is off, loop through the modulator at once every period ms, likely out of sync with the global tempo. tempoDivision and tempoLock are ignored.

  • If tempoSync is on, ignore period, (instead it comes from lx.engine.tempo) and:

    • If tempoLock is off, loop through the modulator at the global Tempo’s rate scaled by the tempoDivision. For example, if the division is Tempo.Division.WHOLE, the modulator will loop every four beats. Since tempoLock is off, this loop is possibly out of phase with the global lx.engine.tempo's beat and measure.
    • If tempoLock is on, do the above in sync with the global tempo’s frequency AND phase by aligning the start of the modulator to the start of the beat. If using a longer division such as Tempo.Division.WHOLE, it will also be synchronized to the start of the measure via the Tempo's beatCount().

Reacting to Real-Time Audio

While using tempo information is an excellent technique for visualizing musical information, raw audio reactivity is another useful technique for material which may or may not conform to any tempo grid.

Audio can be easily used as a modulation source in LX via the modulation system. Audio input can be enabled via the Audio section in the Global controls section on the left-pane.

When the Audio Input is enabled, the control shows real-time audio level metering (lx.audio.meter), as well as individual frequency-band meters (lx.audio.meter.bands) along with smoothing controls.

The response of the meters may be customized using the following controls:

  • audio.meter.gain - how much gain is applied to the input signal

  • audio.meter.range - the total range of the meter in dB

  • audio.meter.attack - determines how quickly the meter responds to new signal

  • audio.meter.release - determines how quickly the meter falls off after signal goes away

  • audio.meter.slope - determines how much the response curve slopes as octaves increase. For most modern music, the meter will show an "even" amount of energy across the spectrum with a slope of something like 3-6dB per octave

To map audio as a modulation source, click the modulation mapping button in the right-pane Modulation section, then choose either the global level meter or one of the individual frequency bands. Both the global meter and the bands will be highlighted in green, indicating that they are valid mapping sources.

Custom audio patterns and effects may also access these meter levels directly in code, using the fields lx.engine.audio.meter and lx.engine.audio.meter.bands.

Detecting Transients with Frequency Analysis

For more precise response to musical information, the Beat Detect modulation device may be used. This may be added in the global modulation section in the right-pane, or to an individual device's modulation section.

This modulation device is similar to the graphic meter, with additional controls to focus on a limited range of frequency bands. The controls from the audio section are replicated here, as well as the following additional controls:

  • Min - beat.minFreq - Minimum frequency the device responds to

  • Max - beat.maxFreq - Maximum frequency the device responds to

  • Threshold - beat.threshold - Sets the threshold level at which a beat is detected. When the average signal level in the monitored range surpasses this level, a beat event is triggered.

  • Floor - beat.floor - Sets a floor value which the audio must drop beneath for a second beat event to fire. This value represents a fraction of the threshold value. For example, if threshold is set to 0.8 and floor is set to 0.5, a beat event will fire when the signal surpasses 0.8. The device will then wait for the signal level to drop below 0.4 (which is threshold * floor) before it will fire another event.

  • Decay - beat.decay - When a beat event is detected, the modulator's output value jumps up to 1 and will then drop back down to 0 over the amount of time specified by the decay parameter.

The device has two outputs which may be used as modulation sources. The main output (meter furthest on the right) outputs the detected beat events. This may also be used as a trigger modulation source.

Alternately, the audio meter on the left displays the ongoing average level of the frequency bands being inspected.