TuningFork.Score (TuningFork v0.1.11)

Copy Markdown View Source

Notes on a beat grid with a tempo map, rendered to one buffer that loops without a break.

Score.from_parts([bass, arpeggio, drums], beats: 64)
|> Score.render(44_100)

Summary

Functions

Place a voice on a beat.

Place the same voice on each of beats.

Where beat falls, in seconds, read through the tempo map.

How long the score runs, in seconds: where :beats falls on the tempo map.

Build a score by mixing parts together.

An empty score.

The frequency of a named note, such as :a3, :fs4 or :eb5.

The frequency interval semitones from name.

Render to signed 16-bit little-endian PCM that loops without a break.

Place a voice every every beats, from from up to but not including :beats.

Which beat seconds falls on, read through the tempo map. The inverse of beat_to_seconds/2.

Change tempo at beat, for everything from there until the next change.

Every tempo this score runs at, as [{beat, bpm}], starting from beat zero.

Types

layer()

@type layer() :: %{fx: keyword(), notes: [{float(), TuningFork.Voice.t()}]}

t()

@type t() :: %TuningFork.Score{
  beats: float(),
  bpm: float(),
  changes: [{float(), float()}],
  layers: [layer()],
  notes: [{float(), TuningFork.Voice.t()}]
}

Functions

add(score, beat, voice)

@spec add(t(), number(), TuningFork.Voice.t()) :: t()

Place a voice on a beat.

add_all(score, beats, voice)

@spec add_all(t(), [number()], TuningFork.Voice.t()) :: t()

Place the same voice on each of beats.

beat_to_seconds(score, beat)

@spec beat_to_seconds(t(), number()) :: float()

Where beat falls, in seconds, read through the tempo map.

duration(score)

@spec duration(t()) :: float()

How long the score runs, in seconds: where :beats falls on the tempo map.

from_parts(parts, opts \\ [])

@spec from_parts([TuningFork.Part.t()], keyword()) :: t()

Build a score by mixing parts together.

Each part is read from its own beat zero. Every part must have the same :bpm; parts that disagree raise ArgumentError.

Options

  • :bpm — the tempo, default the parts'. All parts play at it
  • :beats — how long the score is, default the longest part's TuningFork.Part.beats/1
  • :changes — tempo changes, as new/1 takes them

new(opts \\ [])

@spec new(keyword()) :: t()

An empty score.

Options

  • :bpm — the tempo it starts at, default 120
  • :beats — how long it runs, default 16
  • :changes — tempo changes after the start, as [{beat, bpm}]. Sorted by beat, and where two name the same beat the later one in the list wins

note(name)

@spec note(atom() | number()) :: float()

The frequency of a named note, such as :a3, :fs4 or :eb5.

note(name, interval)

@spec note(atom() | number(), integer()) :: float()

The frequency interval semitones from name.

render(score, rate, opts \\ [])

@spec render(t(), pos_integer(), keyword()) :: binary()

Render to signed 16-bit little-endian PCM that loops without a break.

rate is samples per second. The result is duration/1 seconds long, and at least one frame; anything running past the end is folded back over the start. Each voice lands where its :pan says.

Options

  • :channels — 2 for stereo, the default, or 1 for mono

repeat(score, from, every, voice)

@spec repeat(t(), number(), number(), TuningFork.Voice.t()) :: t()

Place a voice every every beats, from from up to but not including :beats.

Raises ArgumentError unless every is greater than zero.

seconds_to_beat(score, seconds)

@spec seconds_to_beat(t(), number()) :: float()

Which beat seconds falls on, read through the tempo map. The inverse of beat_to_seconds/2.

tempo(score, beat, bpm)

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

Change tempo at beat, for everything from there until the next change.

A change at a beat that already has one replaces it. A score runs at one tempo at a time.

tempo_map(score)

@spec tempo_map(t()) :: [{float(), float()}]

Every tempo this score runs at, as [{beat, bpm}], starting from beat zero.

A change stated at beat zero is the starting tempo and replaces :bpm.