# `Cauldron2D.Audio.Music`
[🔗](https://github.com/jaman/cauldron/blob/v0.2.2/cauldron_2d/lib/cauldron_2d/audio/music.ex#L1)

Music in sections and layers, switched on bar boundaries and mixed per listener.

A game declares a piece — its tempo, its layer names, and for each section what every
layer plays — and then cues it from its own state. The names are the game's; nothing
here knows what a section or a layer is for.

    Cauldron2D.Audio.Music.piece(audio, "orbit", %{
      bpm: 112,
      beats_per_bar: 4,
      layers: [:drums, :bass, :pad],
      sections: %{
        calm: %{drums: calm_drums, bass: calm_bass, pad: pad},
        tense: %{drums: tense_drums, bass: tense_bass, pad: pad}
      },
      fingerprint: "3"
    })

    Cauldron2D.Audio.Music.cue(audio, %{piece: "orbit", section: :tense, layers: %{drums: 1.0, bass: 1.0, pad: 0.4}})

What a layer plays is either performed live or rendered once:

  * a Strudel chain as a string — `~s|s("bd*4").bank("RolandTR909")|` — is performed
    live on the listener's stage. A section's chains play as one `stack`, each layer's
    level applied to its chain as its `velocity` — multiplying whatever `gain` the
    chain sets, never replacing it — and the music level to the whole, sounding notes
    included, at the piece's `bpm / beats_per_bar / 60` cycles per second. Nothing is rendered ahead, so a cue takes effect at once:
    a new section at the next cycle line, new gains on the next block. Every layer of
    a section must be a chain for the section to play this way
  * a `TuningFork.Score`, a zero-arity function returning PCM, or PCM as `{:pcm, binary}` is rendered once,
    two stems at a time in tasks, and kept through `TuningFork.Cache` in the game's
    music directory under
    `piece/section/layer` and a fingerprint of the piece's own, what was written, the
    tempo, the rate and channels — so a changed layer is rendered again. A cue moves its section to the front of
    what is still to render and takes effect when the section is ready — at the next
    bar boundary of the playing section, with a cross-fade; new gains at once

A piece of long recordings — whole tracks rather than loops — sets `render: :on_cue`:
then a section is rendered only when it is first cued, and once another section of the
piece plays, the one before leaves memory (its render stays in the cache, so cueing it
again reads it back).

The music level from `Cauldron2D.Audio.levels/3` scales either.

A director made static (`Cauldron2D.Audio` started with `static_music: true`) plays
the first section it is cued and no other: the section's chains are rendered once,
as one pattern, over the piece's `:loop` cycles (default 8) — whatever still sounds at
the end folded back over the start, so it loops without a seam — kept through
`TuningFork.Cache` under `piece/section/static` and the fingerprint, and looped as a
bed. Every later cue is ignored until `stop/1`. Rendering costs once a section and
rate on a node; playing it back costs a copy a chunk.

# `cue`

```elixir
@type cue() :: %{
  :piece =&gt; String.t(),
  :section =&gt; atom(),
  optional(:layers) =&gt; %{required(atom()) =&gt; number()}
}
```

# `renderable`

```elixir
@type renderable() ::
  String.t() | TuningFork.Score.t() | (-&gt; binary()) | {:pcm, binary()}
```

# `spec`

```elixir
@type spec() :: %{
  :bpm =&gt; number(),
  :beats_per_bar =&gt; pos_integer(),
  :layers =&gt; [atom()],
  :sections =&gt; %{required(atom()) =&gt; %{required(atom()) =&gt; renderable()}},
  optional(:fingerprint) =&gt; String.t(),
  optional(:render) =&gt; :ahead | :on_cue
}
```

# `t`

```elixir
@type t() :: %{
  pieces: map(),
  rendered: map(),
  wanted: cue() | nil,
  playing: {String.t(), atom()} | nil
}
```

# `cue`

```elixir
@spec cue(Cauldron2D.Audio.instance(), cue()) :: :ok
```

Ask for a section of a piece with the given layer gains; see the module rules.

# `current`

```elixir
@spec current(Cauldron2D.Audio.instance()) :: {String.t(), atom()} | nil
```

The piece and section playing, as `{piece, section}`, or `nil`.

# `piece`

```elixir
@spec piece(Cauldron2D.Audio.instance(), String.t(), spec()) :: :ok
```

Declare a piece and start rendering every section of it.

# `prefetch`

```elixir
@spec prefetch([{String.t(), spec()}]) :: :ok
```

Fetch every recording and soundfont `pieces` play, in the background, so that their
first bars are not synthesised while the files arrive. Walks the first 64
cycles of every chain; a chain that does not read is skipped. Returns at once.

# `render_static`

```elixir
@spec render_static(String.t(), spec(), atom(), pos_integer(), 1 | 2, keyword()) ::
  binary()
```

The section of `spec` a static director loops for `piece`, as PCM at `rate` and
`channels`: rendered now and kept if it was not before, read back if it was, in
`:dir` (default `TuningFork.Cache.dir/0`; a game's is
`Cauldron2D.Client.Game.music_dir/1`). A server that will play static music calls
this for its pieces as it starts, so the first listener does not wait for the
rendering.

# `stop`

```elixir
@spec stop(Cauldron2D.Audio.instance()) :: :ok
```

Stop the music.

---

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