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

Rows of live-coded source, and the one pattern they add up to.

    rows = [%{source: "s(\"bd*4\")"}, %{source: "|> gain(0.8)"}, %{source: "-- hh*8"}]
    TuningFork.Session.combined(rows)

# `chain`

```elixir
@type chain() :: {non_neg_integer(), non_neg_integer(), String.t()}
```

A chain of rows folded into the source it makes: `{first row, last row, source}`.

# `row`

```elixir
@type row() :: %{:source =&gt; String.t(), optional(:error) =&gt; String.t() | nil}
```

A row of a session. Only `:source` is required; `checked/1` writes `:error`. Any other key
is left alone.

# `asks?`

```elixir
@spec asks?(row() | String.t()) :: :pianoroll | :scope | nil
```

What a row has asked to be drawn as: `:pianoroll`, `:scope` or `nil`.

Read from the parsed pattern, not from the text. `nil` for a row that is off or will not
parse.

    iex> TuningFork.Session.asks?(%{source: "s(\"bd*4\") |> scope()"})
    :scope
    iex> TuningFork.Session.asks?(%{source: "s(\"bd*4\")"})
    nil

# `chain`

```elixir
@spec chain([row()], non_neg_integer()) :: String.t() | nil
```

The whole source a row belongs to, continuations and all, or `nil` when it plays nothing.

# `checked`

```elixir
@spec checked([row()]) :: [row()]
```

The rows with `:error` set on the ones that will not parse and cleared on the ones that will.

An error is reported on the row a chain starts, trimmed to one line of at most 70
characters.

# `combined`

```elixir
@spec combined([row()]) :: TuningFork.Pattern.t()
```

Every switched-on row stacked into one pattern. A row that will not parse is left out.

# `comment`

```elixir
@spec comment(String.t()) :: String.t()
```

Put a row's marker on, or take off whichever of `off/0` is there.

    iex> TuningFork.Session.comment("s(\"bd\")")
    "-- s(\"bd\")"
    iex> TuningFork.Session.comment("-- s(\"bd\")")
    "s(\"bd\")"
    iex> TuningFork.Session.comment("_ s(\"bd\")")
    "s(\"bd\")"

# `continues?`

```elixir
@spec continues?(row() | String.t()) :: boolean()
```

Whether a row carries on the one above it rather than starting its own: it begins with `|>`.

    iex> TuningFork.Session.continues?(%{source: "  |> scale(\"g:minor\")"})
    true
    iex> TuningFork.Session.continues?(%{source: "s(\"bd\")"})
    false

# `fault`

```elixir
@spec fault(String.t()) :: String.t() | nil
```

Why `source` will not read, in one line, or `nil` when it reads.

    iex> TuningFork.Session.fault("s(\"bd\")")
    nil
    iex> TuningFork.Session.fault("|> gain(0.5)")
    "|> carries on the line above, and there is none"

# `joined`

```elixir
@spec joined([row()]) :: [chain()]
```

The rows folded into the sources they make, as `{first, last, source}` chains.

`first` and `last` are indexes into `rows` as given. Rows that are switched off are left
out; a continuation with nothing live above it is dropped.

Rows that read as Strudel (`TuningFork.Strudel.strudel?/1` on all of them together) are
translated by `TuningFork.Strudel.chains/1` instead, one chain per voice on the rows it
came from. A piece that will not translate is one chain on the row of the fault, whose
source is the whole text.

    iex> TuningFork.Session.joined([%{source: "s(\"bd\")"}, %{source: "|> gain(0.5)"}])
    [{0, 1, "s(\"bd\") |> gain(0.5)"}]

# `live?`

```elixir
@spec live?(row() | String.t()) :: boolean()
```

Whether a row is switched on and has something in it: not blank and not beginning with one
of `off/0`.

    iex> TuningFork.Session.live?(%{source: "s(\"bd\")"})
    true
    iex> TuningFork.Session.live?(%{source: "_ s(\"bd\")"})
    false
    iex> TuningFork.Session.live?(%{source: "   "})
    false

# `off`

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

The markers that switch a row off, longest first.

    iex> TuningFork.Session.off()
    ["--", "//", "_"]

# `tempo`

```elixir
@spec tempo([row()]) :: number() | nil
```

The cycles per second the rows ask for, or `nil` when they do not: Strudel's `setcps`.

---

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