TuningFork.Pattern.Player (TuningFork v0.1.11)

Copy Markdown View Source

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)

Summary

Types

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

t()

Functions

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

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.

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

Where the player has reached, in cycles.

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

Stop everything sounding. The position carries on.

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

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

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

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

Bus zero's {room, roomsize}.

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

Swap the pattern without stopping. The position carries on.

Types

route()

@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()

@type t() :: %TuningFork.Pattern.Player{
  buses: %{required(non_neg_integer()) => 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()) => TuningFork.Reverb.t()},
  sounding: [
    {non_neg_integer(), TuningFork.Voice.Live.t(), :playing | :fading, route()}
  ],
  voice: (term(), float() -> TuningFork.Voice.t() | nil),
  voices: pos_integer()
}

Functions

advance(player, frames, channels \\ 2)

@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(player)

@spec buses(t()) :: %{required(non_neg_integer()) => 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(player, cps)

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

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

cycle(player)

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

Where the player has reached, in cycles.

gain(player, gain)

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

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

hush(player)

@spec hush(t()) :: t()

Stop everything sounding. The position carries on.

new(pattern, rate, opts \\ [])

@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?(player)

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

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

playing(player)

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

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

render(pattern, rate, opts \\ [])

@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(player)

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

Bus zero's {room, roomsize}.

sounding(player)

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

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

update(player, pattern, opts \\ [])

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