Skip to content

Limiter

Turns 48 kHz interleaved stereo 32-bit float audio into 16-bit PCM at a volume, through a look-ahead peak limiter, so audio that would pass full scale is turned down smoothly instead of clipped.

  • A non-finite sample (Inf or NaN) becomes 0 before anything else.
  • The volume, from 0 to 4, multiplies each sample. A change ramps linearly over 20 ms (960 frames), carried across calls of any size; a volume set before the first frame applies at once.
  • The limiter links both channels and looks 5 ms (240 frames) ahead. It keeps every sample within a per-sign ceiling, +32767/32768 for positive samples and -1.0 for negative ones, the values that scale to exactly 32767 and -32768. A reduction ramps in over the look-ahead, no frame's gain is ever above the gain that frame needs, and the gain rises back along a straight line over 50 ms to exactly 1.
  • Each sample is then scaled by 32768 and rounded. A clamp to the 16-bit range backs that up; it never engages, and clampCount counts it for tests.

Audio that never passes the ceiling after the volume comes out bit for bit as a plain conversion would give it, only 240 frames late. process holds those frames back, so it returns fewer frames than it takes until the look-ahead is full, and flush returns them at the end, so as many frames leave as entered.

Limiter.volume  :: number

The volume setVolume last set, from 0 to 4; 1 until then.

Limiter.gain  :: number

The limiter's gain on the last frame that left, from 0 to 1: exactly 1 while nothing is reduced.

Limiter.clampCount  :: number

How many samples the backstop clamp has changed; for tests, since the limiter keeps it at 0.

Limiter.flushed  :: boolean

Whether flush has run, after which process raises.

Limiter.heldFrames  :: number

The frames that entered and have not left yet, up to the 240-frame look-ahead.

Limiter.LOOKAHEAD_FRAMES  :: number

The look-ahead in frames, 240 (5 ms at 48 kHz), on the module: output is late by exactly this much.

Limiter.heard  :: boolean

Whether a frame has been taken at some volume. Until one has, a new volume applies at once.

Limiter.rampFrom  :: number

The volume gain the current ramp started from.

Limiter.rampTo  :: number

The volume the current ramp ends at.

Limiter.rampPosition  :: number

The frames of the current ramp taken so far, up to 960, where the gain is exactly rampTo.

Limiter.delayLeft  :: { number }

The left samples of the held frames, after the volume, in a ring of 240 slots.

Limiter.delayRight  :: { number }

The right samples of the held frames, after the volume, in a ring of 240 slots.

Limiter.delayRequired  :: { number }

The gain each held frame needs to stay within the ceiling, in a ring of 240 slots.

Limiter.delayIndex  :: number

The slot the next frame enters: once 240 frames are held, the slot of the frame that leaves.

Limiter.enteredFrames  :: number

The frames taken in so far, counting the silence flush adds; the next frame's number.

Limiter.minimumFrames  :: { number }

The frame numbers of the candidates for the lowest required gain among the last 241 frames, in a ring of 241 slots, oldest first.

Limiter.minimumGains  :: { number }

The required gains of those candidates, strictly rising, so the first is the lowest.

Limiter.minimumFirst  :: number

The slot of the first candidate.

Limiter.minimumCount  :: number

How many candidates the ring holds.

Limiter.window  :: { number }

The lowest required gain in each of the last 240 windows of 241 frames, one window ending at each frame taken, in a ring of 240 slots. It starts full of 1s, for the windows that end before the audio.

Limiter.windowIndex  :: number

The slot of the oldest entry in window, which the next one replaces.

Limiter.windowSum  :: number

The running sum of window.

Limiter.windowReduced  :: number

How many entries of window are below 1. While none are, the sum is exactly 240 and the attack gain exactly 1.

Limiter.releaseFrom  :: number

The gain the current release rises from.

Limiter.releasePosition  :: number

The frames into the current release, up to 2400, where the release line reaches exactly 1.

Limiter : setVolume ( volume )  -> ()

Sets the volume, a number from 0 to 4, where 1 leaves the audio as it is. A change ramps linearly over the next 20 ms of frames taken; a volume set before the first frame applies at once.

Raises volume must be a number from 0 to 4 for a number outside that range, through checkVolume, keeping the old volume. Callers that take a volume from users check its type first, as Decoder.checkVolume does.

Parameters

volume: number
Limiter : process ( floats ,  frames )  -> buffer

Takes frames frames of 48 kHz interleaved stereo 32-bit float samples from floats and returns the frames that leave, as interleaved stereo 16-bit little-endian PCM. The limiter holds the last 240 frames back, so the result is shorter than the input until that many have entered; blocks of any size give the same audio.

Raises limiter has been flushed after flush, and frames must be a whole number from 0 to <count>, the frames the buffer holds when frames is negative, fractional or more than the buffer holds; a raise changes nothing.

Parameters

floats: buffer
frames: number

Returns

buffer
Limiter : framesNeeded ( outputFrames )  -> number

The frames process must take to return outputFrames: that many, plus the part of the look-ahead not yet held, so the first call takes 240 more than it returns.

Parameters

outputFrames: number

Returns

number
Limiter : flush ( )  -> buffer

Returns the frames still held, as 16-bit PCM, at the end of the audio, and only those: as many frames leave in all as entered, however few that is. They leave as if the audio had gone on in silence. A second call returns an empty buffer.

Returns

buffer
Limiter . checkVolume ( volume )  -> ()

Raises volume must be a number from 0 to 4 unless volume is in that range; NaN and Inf fail too. It owns the range and the message: setVolume uses it, and so does Decoder.checkVolume, which checks the type of a volume that users hand in before it gets here.

Parameters

volume: number
Limiter . new ( )  -> Limiter

Creates a limiter at volume 1 that has taken nothing yet.

Returns

Limiter