# `TuningFork.Filter`
[🔗](https://github.com/jaman/tuning_fork/blob/v0.1.11/tuning_fork/lib/tuning_fork/filter.ex#L1)

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

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

# `kind`

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

# `model`

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

# `state`

```elixir
@opaque state()
```

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

# `t`

```elixir
@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()
}
```

# `cutoff_at`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

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

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

# `step`

```elixir
@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`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
