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

Names to voices: `voice/2` turns a drum name, a note name, a MIDI number, hertz or a map of
controls into a `TuningFork.Voice`.

    iex> %TuningFork.Voice{} = TuningFork.Kit.voice("bd", 0.25)
    iex> TuningFork.Kit.voice("nothing here", 0.25)
    nil

# `banks`

```elixir
@spec banks() :: [String.t()]
```

The sound banks `voice/2` knows, sorted.

    iex> "RolandTR909" in TuningFork.Kit.banks()
    true

These are adjustments to the drums this kit synthesises, so `bank("RolandTR808")` gives the
long booming kick that name is known for while the recording registered as
`RolandTR808_bd` has not arrived, or where there is none. A name not here changes nothing,
so a pattern written for a bank that is not loaded still plays.

# `drums`

```elixir
@spec drums() :: [String.t()]
```

The drum names `voice/2` knows, sorted.

# `families`

```elixir
@spec families() :: keyword([[String.t()]])
```

The drum names grouped by what they are, short name first.

    iex> TuningFork.Kit.families() |> Keyword.keys()
    [:kick, :snare, :hat, :tom, :cymbal, :percussion]

Names in the same group make the same sound; `"bd"` and `"kick"` are one entry.

# `from_map`

```elixir
@spec from_map(map(), number(), keyword()) :: TuningFork.Voice.t() | nil
```

A voice from a map of controls.

`:sound` or `:note` says what to play and is passed to `voice/2`; every other key changes the
voice that comes back. A map with neither is `nil`. A `:bank` plays the recording registered
as `bank_sound` when `TuningFork.Sample.Bank` has one, and is otherwise an adjustment to the
kit's drum (`banks/0`). A bank the kit does not have starts `TuningFork.Strudel.defaults/1`,
the sets such names come from: with `wait: true` (the default) the voice waits for them,
with `wait: false` it is the kit's drum this time.

# `instrument`

```elixir
@spec instrument(String.t(), number(), map(), keyword()) ::
  TuningFork.Part.instrument()
```

A sound as a `TuningFork.Part` instrument: a function that gives `voice/3` of `%{sound:
name, note: note}` for each note it is asked for, and of `%{sound: name}` for `nil`. `opts`
are `voice/3`'s and `controls` any other controls to play it with.

    part(bpm: 100, synth: TuningFork.Kit.instrument("gm_flute", 0.5))
    |> play(:a4, 1)

# `known?`

```elixir
@spec known?(String.t()) :: boolean()
```

Whether `voice/3` plays `name` as a sound rather than a pitch: a drum, a waveform, a GM
instrument or a loaded bank, with or without an index after a colon.

    iex> TuningFork.Kit.known?("gm_epiano1:2")
    true
    iex> TuningFork.Kit.known?("c4")
    false

# `midi`

```elixir
@spec midi(term()) :: integer() | nil
```

The MIDI note a value stands for, or `nil` when it is not a pitch.

    iex> TuningFork.Kit.midi(%{degree: 4, scale: "g:minor"})
    62
    iex> TuningFork.Kit.midi(%{sound: "bd"})
    nil

A note may be a name as a string or atom, a MIDI number, or a map of controls carrying
`:note` or `:degree`. A drum name has no pitch, so it is `nil`. `:octave` moves the root —
3 is where a scale sits without one — and `:transpose` adds semitones after everything
else.

# `notes`

```elixir
@spec notes() :: [String.t()]
```

Every note name `voice/2` takes, low to high.

A name is a letter, `s` for sharp or `b` for flat, and an octave: `c3`, `fs4`, `eb2`.

# `prefetch`

```elixir
@spec prefetch([term()]) :: :ok
```

Start fetching, in the background, every recording and soundfont the `values` would play.
Values are what `voice/3` takes; anything else is skipped. Returns at once.

# `voice`

```elixir
@spec voice(term(), number(), keyword()) :: TuningFork.Voice.t() | nil
```

The voice for `value`, lasting `seconds`.

Returns `nil` for a value naming nothing, so a pattern carrying words this kit does not have
plays the ones it does rather than failing.

## Options

  * `:wait` — whether to wait for a recording still being fetched from the web, default
    `true`; with `false` such a value is `nil` this time and the fetch carries on behind

---

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