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

Sonic Pi's vocabulary: `play`, `sleep`, `sample`, `live_loop`, `with_fx` and the rest.

    use TuningFork.SonicPi

    live_loop :bells do
      sample :perc_bell, rate: rrand(0.125, 1.5)
      sleep rrand(0, 2)
    end

# `at`
*macro* 

Run a block `time` beats from now, alongside: `at 4 do … end`, or `at [1, 2], [:a, :b], fn arg -> … end`
to run it at each time with the matching argument. On a `TuningFork.Part`, `at/2` is the part's.

# `bools`

```elixir
@spec bools(
  number(),
  number(),
  number(),
  number(),
  number(),
  number(),
  number(),
  number()
) :: [
  boolean()
]
```

Numbers as booleans: zero is false.

# `buffer`
*macro* 

A block as a buffer: `buffer do … end` gives the `Buffer` for `render/4` or `play_buffer/2`.

Raises `ArgumentError` when a loop in it will not run.

# `capture`

```elixir
@spec capture((-&gt; term())) ::
  {:ok, TuningFork.SonicPi.Buffer.t()} | {:error, String.t()}
```

Run `fun` as a buffer: what it plays at the top level becomes the score, and every
`live_loop` it starts is collected. Returns what `run/1` returns.

# `choose`

```elixir
@spec choose([term()]) :: term()
```

One element of `list`, or a `TuningFork.Part` choice when the first argument is a part.

# `chord`

```elixir
@spec chord(term(), atom() | String.t() | [term()], keyword() | number()) ::
  [term()] | TuningFork.Part.t()
```

The notes of a chord — `chord(:e3, :minor)` — or a `TuningFork.Part` chord when the first argument is a part.

# `chord_degree`

```elixir
@spec chord_degree(integer(), term(), atom(), pos_integer(), keyword()) :: [integer()]
```

A chord on the `degree`th note of a scale: `count` notes, every other step. `invert:` as `chord/3`.

# `comment`
*macro* 

Skip a block: `comment do … end`.

# `control`

```elixir
@spec control(TuningFork.SonicPi.Handle.t(), keyword()) :: :ok
```

Change a sounding note or an open effect from this moment on.

On a note, `:note`, `:amp` and `:cutoff` move to the new value — at once, or over
`:note_slide`, `:amp_slide` or `:cutoff_slide` seconds. On an effect, the options given
replace the ones it was opened with for everything played afterwards.

# `cue`

```elixir
@spec cue(atom(), keyword()) :: :ok
```

Tell every loop that `name` has happened, as of this round, with `data` for `sync/1` to read.

# `degree`

```elixir
@spec degree(integer(), term(), atom()) :: term()
```

The `degree`th note of `scale_name` from `tonic`, counting from 1.

# `density`
*macro* 

Run a block `count` times at `count` times the tempo: `density 2 do … end`.

# `dice`

```elixir
@spec dice(pos_integer()) :: integer()
```

A whole number from 1 to `sides`, default 6.

# `factor?`

```elixir
@spec factor?(number(), number()) :: boolean()
```

Whether `number` divides by `factor` exactly.

# `fx_names`

```elixir
@spec fx_names() :: [atom()]
```

The `TuningFork.SonicPi.Effects` names, for `with_fx/3`.

# `hush`

```elixir
@spec hush(GenServer.server()) :: :ok
```

Silence a stage: every loop, pattern and sounding voice stopped. `TuningFork.Stage` by default.

# `hz_to_midi`

```elixir
@spec hz_to_midi(number()) :: float()
```

A MIDI number for hertz.

# `in_thread`
*macro* 

Run a block alongside: `in_thread do … end`. It starts now and the thread's own time does not move. Options such as `name:` are accepted and ignored.

# `knit`

```elixir
@spec knit(
  term(),
  non_neg_integer(),
  term(),
  non_neg_integer(),
  term(),
  non_neg_integer(),
  term(),
  non_neg_integer()
) :: [term()]
```

Values repeated: `knit(:a, 2, :b, 1)` is `[:a, :a, :b]`.

# `line`

```elixir
@spec line(number(), number(), keyword()) :: [float()]
```

`steps` numbers from `from` towards `to`, excluding `to` unless `inclusive: true`.

# `live_loop`
*macro* 

Start a named loop that is worked out again every time round: `live_loop :name do … end`.

Takes a `do` block or a `fn`, and an optional keyword list with `:stage`, `:delay` beats,
`:sync` another loop's name, `:auto_cue`. On a stage this returns `:ok` or
`{:error, message}`; inside `run/1` or `buffer/1` the loop is collected.

# `load_sample`

```elixir
@spec load_sample(atom() | String.t()) :: :ok
```

Read a sample into memory ahead of playing it. A family atom loads the whole family.

# `load_samples`

```elixir
@spec load_samples([atom()] | atom()) :: :ok
```

`load_sample/1` for each of a list, or for one.

# `look`

```elixir
@spec look() :: non_neg_integer()
```

What the last `tick` gave, without stepping: `look()`, `look(:name)`, `look(list)`.

# `look`

```elixir
@spec look([term()] | atom()) :: term()
```

# `look`

```elixir
@spec look([term()], atom()) :: term()
```

# `loop`
*macro* 

A loop with no name: `loop do … end`, the same as `live_loop` under a name of its own.

# `midi_to_hz`

```elixir
@spec midi_to_hz(number()) :: float()
```

Hertz for a MIDI number.

# `mirror`

```elixir
@spec mirror([term()]) :: [term()]
```

`list` followed by itself backwards without repeating the last element.

# `note`

```elixir
@spec note(term(), keyword()) :: number() | nil
```

The MIDI number of a note, or `nil` for `nil`; `octave:` moves it to that octave.

# `octs`

```elixir
@spec octs(term(), pos_integer()) :: [integer()]
```

`root` and the same note in the `count - 1` octaves above, as MIDI numbers.

# `one_in`

```elixir
@spec one_in(pos_integer()) :: boolean()
```

True one time in `n`.

# `pick`

```elixir
@spec pick([term()] | TuningFork.Part.t(), non_neg_integer() | [term()]) ::
  [term()] | {[term()], TuningFork.Part.t()}
```

`count` independent choices from `list`, or a `TuningFork.Part` pick when the first argument is a part.

# `play`

```elixir
@spec play(term(), keyword()) :: TuningFork.SonicPi.Handle.t()
```

Sound a note, or a `TuningFork.Part` note when the first argument is a part.

`note` is a MIDI number, a name, hertz as a float, a list for a chord, or `nil` for
nothing. `opts` are `TuningFork.SonicPi.Synth`'s. Returns a `Handle` for `control/2`.

# `play_buffer`

```elixir
@spec play_buffer(String.t() | TuningFork.SonicPi.Buffer.t(), GenServer.server()) ::
  :ok | {:error, String.t()}
```

Start a buffer on a stage: its score once, and each of its loops round and round.

Takes source or a `Buffer`. Returns `:ok`, or `{:error, message}` for source that will not
read.

# `play_chord`

```elixir
@spec play_chord([term()], keyword()) :: TuningFork.SonicPi.Handle.t()
```

Sound every note of `notes` at once.

# `play_pattern`

```elixir
@spec play_pattern([term()], keyword()) :: :ok
```

Sound `notes` one after another, a beat apart.

# `play_pattern_timed`

```elixir
@spec play_pattern_timed([term()], [number()] | number(), keyword()) :: :ok
```

Sound `notes` one after another, each followed by the matching entry of `times` in beats.

`times` is a list read round and round, or one number for every note.

# `print`

```elixir
@spec print(term()) :: :ok
```

Accepted and ignored; a loop cannot print.

# `puts`

```elixir
@spec puts(term()) :: :ok
```

Accepted and ignored; a loop cannot print.

# `rand`

```elixir
@spec rand(number() | Range.t()) :: float()
```

A float from 0 up to `max`, default 1; a range gives one between its ends.

# `rand_i`

```elixir
@spec rand_i(pos_integer()) :: integer()
```

A whole number from 0 up to but not including `max`.

# `range`

```elixir
@spec range(number(), number(), number() | keyword()) :: [number()]
```

Numbers from `from` up to but not including `to`, `step` apart.

A negative `step` counts down. `step` may also be a keyword list: `step: 0.5`, or
`steps: 8` for eight numbers evenly spaced.

# `reflect`

```elixir
@spec reflect([term()]) :: [term()]
```

`list` followed by itself backwards.

# `render`

```elixir
@spec render(
  String.t() | TuningFork.SonicPi.Buffer.t(),
  pos_integer(),
  number(),
  keyword()
) :: binary()
```

Render a buffer to PCM for `seconds`, its loops going round and its score playing once.

`opts` take `:channels`, default 2, and `:limit`, where peaks are rounded off as
`TuningFork.Stage` does, default 0.7; `nil` for none. Raises `ArgumentError` for source that
will not read.

# `ring`

```elixir
@spec ring([term()]) :: [term()]
```

A list, read round and round by `tick/1`, `look/1` and `ring_at/2`.

# `ring_at`

```elixir
@spec ring_at([term()], integer()) :: term()
```

The element of `list` at `index`, wrapping in both directions.

# `rotate`

```elixir
@spec rotate([term()], integer()) :: [term()]
```

`list` with its first `count` elements moved to the end.

# `rrand`

```elixir
@spec rrand(number(), number(), keyword()) :: float()
```

A float from `low` up to `high`, from the thread's generator; `res:` rounds it to a step.

# `rrand_i`

```elixir
@spec rrand_i(integer(), integer()) :: integer()
```

A whole number from `low` to `high`, both included.

# `run`

```elixir
@spec run(String.t()) :: {:ok, TuningFork.SonicPi.Buffer.t()} | {:error, String.t()}
```

Read a buffer of Sonic Pi-shaped source.

Top-level code becomes `score`, or `nil` when nothing was played at the top level; every
`live_loop` becomes `{name, first_round, body}`, where `body` gives a fresh round each call
as `TuningFork.Stage.start_loop/4` wants. `{:error, message}` when the source will not
read or a loop will not run, naming the loop.

# `run_round`

```elixir
@spec run_round((-&gt; term())) :: {:ok, TuningFork.Score.t()} | {:error, String.t()}
```

Run `fun` as one round of a loop and give the score it played. See `TuningFork.SonicPi.Blocks.run_round/1`.

# `sample`

```elixir
@spec sample(atom() | String.t() | TuningFork.Sample.t(), keyword()) ::
  TuningFork.SonicPi.Handle.t()
```

Sound a recording: a name from `TuningFork.Sample.Bank`, a path, or a `TuningFork.Sample`.

## Options

  * `:rate` — speed and pitch together, 1 as recorded, negative backwards
  * `:rpitch` — the same as a number of semitones
  * `:beat_stretch` — a rate that makes it last this many beats
  * `:start`, `:finish` — the part to play, as fractions from 0 to 1
  * `:amp`, `:pan`, `:attack`, `:decay`, `:sustain`, `:release`, `:cutoff`, `:res` — as `play/2`

Raises `ArgumentError` for a name the bank does not have.

# `sample_duration`

```elixir
@spec sample_duration(atom() | String.t() | TuningFork.Sample.t(), keyword()) ::
  float()
```

How long `sample/2` would sound `name` for with `opts`, in seconds.

# `sample_names`

```elixir
@spec sample_names(atom() | nil) :: [atom()]
```

Every bank sample name, or those in one family: `sample_names(:ambi)`.

# `scale`

```elixir
@spec scale(term(), atom() | String.t(), keyword()) :: [term()]
```

The notes of a scale, one octave up from `root` and the octave note above.

# `shuffle`

```elixir
@spec shuffle([term()]) :: [term()]
```

`list` in a random order.

# `sleep`

```elixir
@spec sleep(number()) :: :ok
```

Move the thread on by `beats`. Outside a round, with a stage running, this waits that long.

# `spread`

```elixir
@spec spread(non_neg_integer(), pos_integer()) :: [boolean()]
```

`hits` beats spread as evenly as they go over `steps`, as booleans — a Euclidean rhythm.

# `stop`

```elixir
@spec stop() :: no_return()
```

End the round here. Nothing after it is played.

# `stretch`

```elixir
@spec stretch([term()], pos_integer()) :: [term()]
```

Each element of `list` repeated `count` times in place.

# `sync`

```elixir
@spec sync(atom()) :: keyword()
```

Wait for `name` to have been cued, and give what it was cued with.

Once it has, this returns at once. Until then the round is given up with
`{:error, "waiting for name"}`, and the loop tries again next time round.

# `synth`

```elixir
@spec synth(atom() | TuningFork.Part.t(), keyword() | TuningFork.Voice.t()) ::
  TuningFork.SonicPi.Handle.t() | TuningFork.Part.t()
```

Sound a note with a named synth without changing the thread's, or set a part's synth.

# `synth_names`

```elixir
@spec synth_names() :: [atom()]
```

The `TuningFork.SonicPi.Synth` names, for `use_synth/1`.

# `tick`

```elixir
@spec tick() :: non_neg_integer()
```

Step a counter and give what it was, or the element of a list that count picks.

`tick()` and `tick(:name)` are `TuningFork.Tick`'s; `tick(list)` reads `list` at the default
counter's count and `tick(list, :name)` at a named one.

# `tick`

```elixir
@spec tick([term()] | atom()) :: term()
```

# `tick`

```elixir
@spec tick([term()], atom()) :: term()
```

# `tick_reset`

```elixir
@spec tick_reset(atom() | nil) :: :ok
```

Put the default counter, or the named one, back to zero.

# `tick_reset_all`

```elixir
@spec tick_reset_all() :: :ok
```

Put every counter back to zero.

# `tick_set`

```elixir
@spec tick_set(atom() | nil, non_neg_integer()) :: :ok
```

Put a counter at `count`, so `look` gives `count` and the next `tick` gives one more.

# `times`
*macro* 

Run a block `count` times: `times 4 do … end`, or `times(4, fn pass -> … end)` to be told which pass.

# `uncomment`
*macro* 

Run a block as it is: `uncomment do … end`.

# `use_bpm`

```elixir
@spec use_bpm(number()) :: :ok
```

Beats a minute for everything after this point in the thread. Default 60.

# `use_debug`

```elixir
@spec use_debug(boolean()) :: :ok
```

Accepted and ignored; there is no debug output to switch.

# `use_merged_synth_defaults`

```elixir
@spec use_merged_synth_defaults(keyword()) :: :ok
```

Options every `play/2` from here on starts from, merged over the ones there already.

# `use_random_seed`

```elixir
@spec use_random_seed(term()) :: :ok
```

Seed the thread's generator, so what follows draws the same numbers every time.

# `use_sample_bpm`

```elixir
@spec use_sample_bpm(atom() | String.t() | TuningFork.Sample.t(), keyword()) :: :ok
```

Set the tempo so that `name` lasts `num_beats:` beats, default 1.

# `use_sample_defaults`

```elixir
@spec use_sample_defaults(keyword()) :: :ok
```

Options every `sample/2` from here on starts from.

# `use_synth`

```elixir
@spec use_synth(atom() | String.t() | TuningFork.Voice.t()) :: :ok
```

The synth `play/2` uses from here on: a name from `TuningFork.SonicPi.Synth.names/0`, a
`TuningFork.Voice`, or a sound name `TuningFork.Kit.known?/1` accepts, such as `"gm_epiano1"`,
played at each note through the kit.

# `use_synth_defaults`

```elixir
@spec use_synth_defaults(keyword()) :: :ok
```

Options every `play/2` from here on starts from. A `play` option overrides one here.

# `use_transpose`

```elixir
@spec use_transpose(integer()) :: :ok
```

Semitones added to every note from here on.

# `with_bpm`
*macro* 

Run a block at `bpm`, then go back to the tempo before: `with_bpm 120 do … end`.

# `with_fx`
*macro* 

Run a block with everything it plays going through an effect: `with_fx :reverb, mix: 0.3 do … end`.

`name` is one of `fx_names/0`. A `fn` taking one argument is given the effect's `Handle`,
for `control/2`.

# `with_random_seed`
*macro* 

Run a block with the generator seeded from `seed`, then carry on from where it was.

# `with_synth`
*macro* 

Run a block with another synth, then go back: `with_synth :saw do … end`.

# `with_synth_defaults`
*macro* 

Run a block with other synth defaults, then go back.

# `with_transpose`
*macro* 

Run a block transposed by `semitones`, then go back: `with_transpose 12 do … end`.

---

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