A step sequencer as a struct and pure functions over it.
project =
Composer.new(bpm: 100)
|> Composer.add_track(kind: :drum, sound: "kick")
|> Composer.toggle_step(0, 0)
|> Composer.toggle_step(0, 4)
Composer.to_source(project)
Composer.to_score(project)
Summary
Functions
Add a track at the end.
The voice every instrument in the piece is derived from.
How long the whole composition runs, in beats.
Empty a track's steps, keeping the track.
Move a step one degree up (1) or down (-1), turning it off past either end.
A four-track demo composition: kick, snare, hat and a bass line.
The General MIDI note a named drum sounds on.
The drums a grid can reach, as {id, label}.
The General MIDI soundfont, by its gm_ name, a named instrument plays with.
Lengthen or shorten the note at step by one, within what room it has.
The instruments a grid can reach, as {id, label}.
The name TuningFork.Kit and the drum machine banks give a named drum.
Move a track up (-1) or down (1). A move past either end is a no-op.
A composition. Takes any field of the struct as an option.
Which repeats of the grid a track plays, one character per bar: x plays, anything else
rests. A track's :plays is cycled to the piece's bars; nil or "" plays them all.
The General MIDI program a named instrument selects.
Remove a track.
The notes a pitched track may reach, low to high.
The scales a grid can reach.
Change a setting. field must be one of settings/0; anything else is a no-op.
Hold a note over length steps.
Set a step to a scale degree, clamped to what the scale holds. 0 silences it.
The settings set/3 will accept.
How long one step lasts, in beats.
How many steps wide the grid is: beats to a bar times steps to a beat.
The composition as a TuningFork.Score, for playing without evaluating source.
The composition as TuningFork.Part source, or "" if nothing would sound.
Silence a track without losing it, or bring it back.
Turn a step on or off, keeping whatever length it had.
A track by position, or nil.
How many tracks there are.
Change a track's fields. changes takes any field TuningFork.Composer.Track.new/2 accepts.
The voice a track plays with: a TuningFork.Voice, or with a recorded :kit a
TuningFork.Part instrument asked for one per note.
Types
@type t() :: %TuningFork.Composer{ bars: pos_integer(), bpm: pos_integer(), division: pos_integer(), gain: float(), kit: :synth | String.t(), meter: pos_integer(), name: String.t(), reverb: float(), root: atom(), scale: atom(), tracks: [TuningFork.Composer.Track.t()] }
Functions
Add a track at the end.
@spec base_voice(t()) :: TuningFork.Voice.t()
The voice every instrument in the piece is derived from.
@spec beats(t()) :: pos_integer()
How long the whole composition runs, in beats.
@spec clear_track(t(), non_neg_integer()) :: t()
Empty a track's steps, keeping the track.
@spec cycle_step(t(), non_neg_integer(), non_neg_integer(), -1 | 1) :: t()
Move a step one degree up (1) or down (-1), turning it off past either end.
Composer.cycle_step(project, 0, 4, 1)
@spec demo() :: t()
A four-track demo composition: kick, snare, hat and a bass line.
@spec drum_note(String.t() | nil) :: pos_integer()
The General MIDI note a named drum sounds on.
The drums a grid can reach, as {id, label}.
The General MIDI soundfont, by its gm_ name, a named instrument plays with.
@spec grow(t(), non_neg_integer(), non_neg_integer(), -1 | 1) :: t()
Lengthen or shorten the note at step by one, within what room it has.
The instruments a grid can reach, as {id, label}.
The name TuningFork.Kit and the drum machine banks give a named drum.
@spec move_track(t(), non_neg_integer(), -1 | 1) :: t()
Move a track up (-1) or down (1). A move past either end is a no-op.
A composition. Takes any field of the struct as an option.
Fields and defaults: :bpm (96), :bars (2), :meter beats in a bar (4),
:division steps in a beat (4), :root note name (:a2), :scale one of scales/0
(:minor_pentatonic), :gain 0.0–1.0 (0.5), :reverb room size 0.0–1.0 with 0.0 off
(0.0), :kit :synth for the kit's own drums and General MIDI synth voices, or a drum
machine name such as "RolandTR909" for its recordings and the General MIDI soundfonts
(:synth), :name the variable to_source/1 binds the score to ("song"), :tracks a
list of TuningFork.Composer.Track ([]). Every track given is resized to the grid width.
Composer.new()
Composer.new(bpm: 120, meter: 3, root: :c3)
@spec passes(TuningFork.Composer.Track.t(), t()) :: String.t()
Which repeats of the grid a track plays, one character per bar: x plays, anything else
rests. A track's :plays is cycled to the piece's bars; nil or "" plays them all.
iex> TuningFork.Composer.passes(%TuningFork.Composer.Track{plays: "x."}, TuningFork.Composer.new(bars: 5))
"x.x.x"
@spec program_for(String.t() | nil) :: 0..127
The General MIDI program a named instrument selects.
@spec remove_track(t(), non_neg_integer()) :: t()
Remove a track.
The notes a pitched track may reach, low to high.
@spec scales() :: [atom()]
The scales a grid can reach.
Change a setting. field must be one of settings/0; anything else is a no-op.
value may be a number or a string. Nothing raises:
:bpm,:bars,:meter,:divisionare truncated to an integer and clamped to at least 1.:gainand:reverbare clamped to 0.0–1.0.- A number that will not parse leaves the setting as it was.
:rootand:scaletake a string or an atom. A string becomes an atom unchecked.:nametakes anythingto_string/1accepts.
Setting :meter or :division resizes every track to the new grid width.
Composer.set(project, :bpm, 120)
Composer.set(project, :bpm, "120")
Composer.set(project, :meter, 3)
@spec set_length(t(), non_neg_integer(), non_neg_integer(), pos_integer()) :: t()
Hold a note over length steps.
step may be anywhere inside the note, not only its first step. Clamped so the note stops
where the next one starts. A step with no note is left alone.
@spec set_step(t(), non_neg_integer(), non_neg_integer(), non_neg_integer()) :: t()
Set a step to a scale degree, clamped to what the scale holds. 0 silences it.
@spec settings() :: [atom()]
The settings set/3 will accept.
How long one step lasts, in beats.
@spec steps_per_bar(t()) :: pos_integer()
How many steps wide the grid is: beats to a bar times steps to a beat.
@spec to_score(t()) :: TuningFork.Score.t()
The composition as a TuningFork.Score, for playing without evaluating source.
project |> Composer.to_score() |> TuningFork.Score.render(44_100)
The composition as TuningFork.Part source, or "" if nothing would sound.
Takes TuningFork.Composer.Source's options, notably :output.
@spec toggle_mute(t(), non_neg_integer()) :: t()
Silence a track without losing it, or bring it back.
@spec toggle_step(t(), non_neg_integer(), non_neg_integer()) :: t()
Turn a step on or off, keeping whatever length it had.
@spec track(t(), non_neg_integer()) :: TuningFork.Composer.Track.t() | nil
A track by position, or nil.
@spec track_count(t()) :: non_neg_integer()
How many tracks there are.
@spec update_track(t(), non_neg_integer(), keyword() | map()) :: t()
Change a track's fields. changes takes any field TuningFork.Composer.Track.new/2 accepts.
Composer.update_track(project, 0, gain: 0.4, muted: true)Switching :kind to :drum flattens every degree to a plain hit.
@spec voice_for(TuningFork.Composer.Track.t(), t()) :: TuningFork.Voice.t() | TuningFork.Part.instrument()
The voice a track plays with: a TuningFork.Voice, or with a recorded :kit a
TuningFork.Part instrument asked for one per note.