TuningFork.Filter (TuningFork v0.1.11)

Copy Markdown View Source

A resonant filter, cut off at a frequency in hertz.

TuningFork.Filter.new(hz: 800, q: 6.0, poles: 4)

Summary

Types

What a filter carries between samples. Opaque; make one with start/1.

t()

Functions

Where the cutoff sits at seconds into a note, in hertz, with the envelope and curve applied. Without either this is :hz.

The gain the signal is pushed into the ladder with: e to the power of :drive, held between 0.1 and 2000.0.

How hard the ladder's resonance is fed back: :q scaled by 0.13 and held at 8.0.

What the ladder's output is multiplied by: 1 / drive/1 times 1 + feedback/1, the second factor held at 1.75.

A filter from options.

The state a filter starts from, with nothing yet stored in it.

One sample through the filter, and the state to carry to the next.

Types

kind()

@type kind() :: :lowpass | :highpass | :bandpass

model()

@type model() :: :ladder | :svf

state()

@opaque state()

What a filter carries between samples. Opaque; make one with start/1.

t()

@type t() :: %TuningFork.Filter{
  amount: float(),
  curve: TuningFork.Curve.t() | nil,
  drive: float(),
  envelope: TuningFork.Envelope.t() | nil,
  hz: float(),
  kind: kind(),
  model: model(),
  poles: 2 | 4,
  q: float()
}

Functions

cutoff_at(filter, at)

@spec cutoff_at(t(), number()) :: float()

Where the cutoff sits at seconds into a note, in hertz, with the envelope and curve applied. Without either this is :hz.

drive(filter)

@spec drive(t()) :: float()

The gain the signal is pushed into the ladder with: e to the power of :drive, held between 0.1 and 2000.0.

feedback(filter)

@spec feedback(t()) :: float()

How hard the ladder's resonance is fed back: :q scaled by 0.13 and held at 8.0.

iex> TuningFork.Filter.feedback(TuningFork.Filter.new(q: 9.0))
1.17
iex> TuningFork.Filter.feedback(TuningFork.Filter.new(q: 200.0))
8.0

makeup(filter)

@spec makeup(t()) :: float()

What the ladder's output is multiplied by: 1 / drive/1 times 1 + feedback/1, the second factor held at 1.75.

iex> TuningFork.Filter.makeup(TuningFork.Filter.new(drive: 0.0, q: 0.0))
1.0

new(opts \\ [])

@spec new(keyword()) :: t()

A filter from options.

Options

  • :kind:lowpass, :highpass or :bandpass, default :lowpass
  • :model:ladder or :svf, default :ladder. A :highpass or :bandpass is always :svf
  • :hz — the cutoff in hertz, default 1200.0
  • :q — resonance, 0 and up; default 1.0. Values past about 62 are all as resonant as it goes
  • :drive — ladder saturation, as an exponent; default 0.69. :svf ignores it
  • :poles — 2 or 4, for :svf only; default 4. The ladder is always four
  • :envelope — a TuningFork.Envelope that sweeps the cutoff, or nil
  • :amount — how far the envelope sweeps, in octaves above :hz; default 0.0
  • :curve — a TuningFork.Curve over seconds into the note, multiplying the cutoff, or nil

start(filter)

@spec start(t() | nil) :: state()

The state a filter starts from, with nothing yet stored in it.

step(filter, sample, state, at, rate)

@spec step(t(), float(), state(), number(), pos_integer()) :: {float(), state()}

One sample through the filter, and the state to carry to the next.

at is how far into the note this sample is, in seconds; the envelope and curve are read against it. rate is samples per second. The cutoff is held between 20 Hz and rate / 2.2.