# `TuningFork.Pattern.Player`
[🔗](https://github.com/jaman/tuning_fork/blob/v0.1.11/tuning_fork/lib/tuning_fork/pattern/player.ex#L1)

Walks a pattern in time, rendering the events that come due to PCM block by block.

    player = TuningFork.Pattern.Player.new(pattern, 44_100, cps: 0.5)
    {pcm, player} = TuningFork.Pattern.Player.advance(player, 512, 2)

# `route`

```elixir
@type route() :: {non_neg_integer(), float()}
```

Where a voice goes: its orbit, and how much of it is sent to that orbit's room.

# `t`

```elixir
@type t() :: %TuningFork.Pattern.Player{
  buses: %{required(non_neg_integer()) =&gt; map()},
  cps: float(),
  cycle: float(),
  gain: float(),
  next: {TuningFork.Pattern.t(), :cycle} | nil,
  parallel: term(),
  pattern: TuningFork.Pattern.t(),
  pending: [TuningFork.Pattern.event()],
  rate: pos_integer(),
  reverbs: %{required(non_neg_integer()) =&gt; TuningFork.Reverb.t()},
  sounding: [
    {non_neg_integer(), TuningFork.Voice.Live.t(), :playing | :fading, route()}
  ],
  voice: (term(), float() -&gt; TuningFork.Voice.t() | nil),
  voices: pos_integer()
}
```

# `advance`

```elixir
@spec advance(t(), pos_integer(), pos_integer()) :: {binary(), t()}
```

The next `frames` frames, and the player advanced past them.

The result is always exactly `frames` frames for `channels` channels. Events beginning inside
the block start at their own sample rather than at the block boundary.

# `buses`

```elixir
@spec buses(t()) :: %{required(non_neg_integer()) =&gt; map()}
```

What each bus is set to, keyed by orbit: a map of `room`, `roomsize`, `postgain`, `xfade`
and `compressor`. An orbit the pattern has never mentioned has no entry.

# `cps`

```elixir
@spec cps(t(), number()) :: t()
```

Run at a different speed from the next block on. The position carries on.

# `cycle`

```elixir
@spec cycle(t()) :: float()
```

Where the player has reached, in cycles.

# `gain`

```elixir
@spec gain(t(), number()) :: t()
```

Scale everything the player puts out, sounding notes included, by `gain`; `0.0` is silence.

# `hush`

```elixir
@spec hush(t()) :: t()
```

Stop everything sounding. The position carries on.

# `new`

```elixir
@spec new(TuningFork.Pattern.t(), pos_integer(), keyword()) :: t()
```

A player at cycle zero of `pattern`, running at `rate` samples per second.

## Options

  * `:cps` — cycles per second, default 0.5
  * `:voice` — a function from a value and a length in seconds to a `TuningFork.Voice` or
    `nil`. Default `TuningFork.Kit.voice/2`
  * `:voices` — most notes sounding at once, default 32. Past that the oldest are faded out
    over 10 ms
  * `:parallel` — render the sounding voices on every core (`Task.async_stream`); the
    same output, sooner, at the cost of a task per voice per block. For one player on
    a machine of its own; not for many players sharing a server. Default `false`

# `pending?`

```elixir
@spec pending?(t()) :: boolean()
```

Whether a pattern is waiting for the next cycle line to come in.

# `playing`

```elixir
@spec playing(t()) :: non_neg_integer()
```

How many notes are sounding and not fading out. Never more than the `:voices` cap.

# `render`

```elixir
@spec render(TuningFork.Pattern.t(), pos_integer(), keyword()) :: binary()
```

Render `cycles` of a pattern to signed 16-bit little-endian PCM, without a sound device.

    pcm = Player.render(pattern, 44_100, cycles: 4, cps: 0.5)

The result is exactly `cycles` cycles long; whatever is still ringing at the end is folded
back over the beginning.

## Options

  * `:cycles` — how many to render, default 4
  * `:cps` — cycles per second, default 0.5
  * `:channels` — 2 for stereo, the default
  * `:tail` — seconds rendered past the last cycle and folded back over the start, default 1.0
  * `:voice`, `:voices` — as `new/3` takes them

# `room`

```elixir
@spec room(t()) :: {float(), float()}
```

Bus zero's `{room, roomsize}`.

# `sounding`

```elixir
@spec sounding(t()) :: non_neg_integer()
```

How many notes are sounding right now, the ones fading out included.

# `update`

```elixir
@spec update(t(), TuningFork.Pattern.t(), keyword()) :: t()
```

Swap the pattern without stopping. The position carries on.

`at: :cycle`, the default, holds it until the next cycle line. `at: :now` takes effect on the
next block. Notes already sounding finish as the pattern read when they started.

---

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