TuningFork.Part (TuningFork v0.1.11)

Copy Markdown View Source

One line of music, written by moving a cursor measured in beats.

import TuningFork.Part

bass =
  part(bpm: 112, synth: soft_bass)
  |> play(:d2, 1.5)
  |> play(:a2, 0.5)

Summary

Types

A voice for each note: called with the note, or nil for a hit with no pitch.

t()

Functions

Put the cursor at an exact beat, forwards or back.

How far the part runs, in beats.

A number from low up to but not including high, and the part with its generator advanced.

Play every note of a chord at once, then move the cursor on by step beats.

Where the cursor is, in beats.

Change the part's level for everything after this point.

Call fun with the part with the given probability, and move on by step beats either way.

The part's notes, as {beat, voice}, in the order they were played.

Move the part in the stereo field for everything after this point.

An empty part with its cursor at beat zero. Every part starts from its own beat zero; one that comes in later begins with rest/2.

Play a run of notes, each step beats apart.

count independent choices from list, and the part with its generator advanced. The result may repeat an element.

Play a note at the cursor and move the cursor on by step beats.

Play one of notes, chosen by the part's own generator, and move on by step beats.

Apply fun to the part times over, from wherever the cursor is.

Apply fun to the part times over, told which pass it is, counting from zero.

Move the cursor on by beats without playing anything.

Play a pattern of evenly spaced steps, each step beats apart.

Change the voice, or instrument/0, used for everything after this point.

Play a note without moving the cursor, for stacking a chord. Takes play/4's options.

Types

instrument()

@type instrument() :: (atom() | number() | nil -> TuningFork.Voice.t())

A voice for each note: called with the note, or nil for a hit with no pitch.

step_entry()

@type step_entry() ::
  atom()
  | number()
  | TuningFork.Voice.t()
  | nil
  | {atom() | number() | TuningFork.Voice.t(), keyword()}

t()

@type t() :: %TuningFork.Part{
  bpm: float(),
  cursor: float(),
  fx: keyword(),
  gain: float(),
  notes: [{float(), TuningFork.Voice.t()}],
  pan: float(),
  rand: TuningFork.Rand.t(),
  synth: TuningFork.Voice.t() | instrument() | nil
}

Functions

at(part, beat)

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

Put the cursor at an exact beat, forwards or back.

beats(part)

@spec beats(t()) :: float()

How far the part runs, in beats.

The later of the cursor and the end of its last-ringing note.

between(part, low, high)

@spec between(t(), number(), number()) :: {float(), t()}

A number from low up to but not including high, and the part with its generator advanced.

chord(part, notes, step \\ 1.0, opts \\ [])

@spec chord(t(), [atom() | number()], number(), keyword()) :: t()

Play every note of a chord at once, then move the cursor on by step beats.

notes is a list of note names or frequencies. opts are play/4's and apply to every note of the chord.

cursor(part)

@spec cursor(t()) :: float()

Where the cursor is, in beats.

gain(part, gain)

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

Change the part's level for everything after this point.

maybe(part, probability, fun, step \\ 0.0)

@spec maybe(t(), float(), (t() -> t()), number()) :: t()

Call fun with the part with the given probability, and move on by step beats either way.

probability runs 0.0 to 1.0 and is drawn from the part's own generator, which advances whether or not fun is called.

notes(part)

@spec notes(t()) :: [{float(), TuningFork.Voice.t()}]

The part's notes, as {beat, voice}, in the order they were played.

pan(part, pan)

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

Move the part in the stereo field for everything after this point.

Clamped to -1.0 to 1.0.

part(opts \\ [])

@spec part(keyword()) :: t()

An empty part with its cursor at beat zero. Every part starts from its own beat zero; one that comes in later begins with rest/2.

Options

  • :synth — the voice its notes are played with, or a instrument/0 asked for one per note; default nil, in which case TuningFork.Voice.new/1 is used per note
  • :bpm — its tempo, default 120
  • :gain — a level over everything in it, default 1.0
  • :pan — where it sits, -1.0 hard left to 1.0 hard right, default 0.0 centred
  • :fx — effects applied to this part alone, as [echo: [...], reverb: [...]], default none
  • :seed — seeds the part's own random generator, default 1

pattern(part, notes, step \\ 1.0, opts \\ [])

@spec pattern(t(), [atom() | number()], number() | [number()], keyword()) :: t()

Play a run of notes, each step beats apart.

step may be a list, whose entries are used in turn and repeat: [0.5, 0.5, 1.0] is two short and one long, over and over. opts are play/4's and apply to every note.

pick(part, list, count \\ 1)

@spec pick(t(), [term()], non_neg_integer()) :: {[term()], t()}

count independent choices from list, and the part with its generator advanced. The result may repeat an element.

play(part, note, step \\ 1.0, opts \\ [])

@spec play(t(), atom() | number() | TuningFork.Voice.t(), number(), keyword()) :: t()

Play a note at the cursor and move the cursor on by step beats.

note may be a note name, a frequency in Hz, or a whole TuningFork.Voice, which is played as it stands without being pitched. step is how long until the next note, in beats, independent of how long this note sounds.

Options

  • :release — how long the note sounds, in beats. Longer than step overlaps the next
  • :gain — this note's level, multiplied by the part's
  • :pan — how far this note sits from where the part does, -1.0 to 1.0, added to the part's and clamped to that range
  • :bend — semitones to arrive at by the end of the note; 2 bends up a tone
  • :curves — modulation in full, as TuningFork.Voice takes it. A :freq curve given here supersedes :bend
  • :synth — a voice, or a instrument/0, for this note only, overriding the part's

Any option value of the form {:between, low, high} is drawn from the part's own generator for each note.

|> play(:e4, 0.5, release: 3.0)
|> play(:e4, 1.0, bend: 2)
|> play(:e4, 1.0, curves: %{gain: Curve.linear(0.2, 1.0)})

play_any(part, notes, step \\ 1.0, opts \\ [])

@spec play_any(t(), [atom() | number() | TuningFork.Voice.t()], number(), keyword()) ::
  t()

Play one of notes, chosen by the part's own generator, and move on by step beats.

The same seed picks the same notes in the same order. opts are play/4's.

repeat(part, passes, fun)

@spec repeat(t(), pos_integer() | String.t(), (t() -> t())) :: t()

Apply fun to the part times over, from wherever the cursor is.

times may instead be a string of x and ., one per pass: fun is applied on an x, and on a . the cursor moves on by as much as fun would have moved it, playing nothing.

|> repeat(4, &steps(&1, "x...x..."))
|> repeat("..xx", &steps(&1, "x...x..."))

repeat_indexed(part, times, fun)

@spec repeat_indexed(t(), pos_integer(), (t(), non_neg_integer() -> t())) :: t()

Apply fun to the part times over, told which pass it is, counting from zero.

rest(part, beats)

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

Move the cursor on by beats without playing anything.

steps(part, pattern, step \\ 0.25, opts \\ [])

@spec steps(t(), String.t() | [step_entry()], number(), keyword()) :: t()

Play a pattern of evenly spaced steps, each step beats apart.

A string plays the part's own synth on x or X, plays at a ninth of full level on a digit 1 to 9 (multiplied into any :gain given), and rests on any other character. Spaces are ignored. step defaults to 0.25.

|> steps("x..x..x.")
|> steps("x2x2x9x2", 0.25)

A list places a different entry on each step. An entry is anything play/4 accepts, nil for a rest, or {note, opts} for one step carrying its own options merged over opts:

|> steps([{:a3, release: 4.0}, nil, :c4, nil, :e4, nil, nil, nil])

synth(part, voice)

@spec synth(t(), TuningFork.Voice.t() | instrument()) :: t()

Change the voice, or instrument/0, used for everything after this point.

under(part, note, opts \\ [])

@spec under(t(), atom() | number() | TuningFork.Voice.t(), keyword()) :: t()

Play a note without moving the cursor, for stacking a chord. Takes play/4's options.