Packages

Twelve-tone rows in Elixir

Current section

Files

Jump to
webern lib webern.ex
Raw

lib/webern.ex

defmodule Webern do
@moduledoc """
Main entrypoint into the `Webern` functionality.
## Creating objects
`row/1` provides a helper function to generate a new tone row consisting
of pitch classes from the 12-tone semitone chromatic scale
`matrix/1` accepts a row as an argument and returns a displayable matrix
based on the row.
## Transforming tone rows
`Webern` also provides functions for the standard serial transformations:
* `prime` - the original form of the row
* `retrograde` - the row backwards
* if the prime form begins with pitch class indices [3, 5], the retrograde
form will end with pitch class indices [5, 3]
* `inverse` - the row with the interval between each set of pitch classes
flipped
* if the first two pitch classes in the prime form of the row are [3, 5],
the first two pitch classes of the inverse form will be [3, 1];
the upward step of 2 semitones becomes a downward step of 2 semitones
* `retrograde_inverse` - a compound transformation, acheived by taking first
the inverse of the prime form, and then the retrograde of the resulting
form
* `inverse_retrograde` - another, less common compound form, acheived by
taking first the retrograde of the prime form, and then the inverse of
the resulting form
For more information about twelve tone technique and transformations, see
[Twelve-tone technique](https://en.wikipedia.org/wiki/Twelve-tone_technique)
on Wikipedia
"""
alias Webern.{Row, Matrix, Lilypond}
@type row :: Row.t
@type matrix :: Matrix.t
@doc """
Create a `Webern.Row` from the provided `source_row`.
The input `source_row` should be a 12-tone row defined by an ordering of
the integers `0`..`11`.
A shorter row can also be supplied, but the modulo
point of the generated row will still be 12, allowing the possibility for
row permutations to include pitches not present in the original row.
"""
@spec row([integer]) :: row
def row(source_row) when is_list(source_row) do
Row.new(source_row)
end
@doc """
Returns the prime form of `row`, optionally transposed
to begin at pitch class `start`.
## Example
iex> row = Webern.Row.new([0, 2, 1, 3, 4, 6, 5, 7, 8, 10, 9, 11])
iex> Webern.prime(row)
%Webern.Row{
pitch_classes: [0, 2, 1, 3, 4, 6, 5, 7, 8, 10, 9, 11]
}
iex> Webern.prime(row, 3)
%Webern.Row{
pitch_classes: [3, 5, 4, 6, 7, 9, 8, 10, 11, 1, 0, 2]
}
See [above](#module-transforming-tone-rows) for details about the prime form
"""
@spec prime(row, integer | nil) :: row
def prime(row = %Webern.Row{}, start \\ nil) do
with start <- start || List.first(row.pitch_classes) do
Row.prime(row, start)
end
end
@doc """
Returns the retrograde form of `row`, with the prime form optionally
transposed to begin at pitch class `start`.
iex> row = Webern.Row.new([0, 2, 1, 3, 4, 6, 5, 7, 8, 10, 9, 11])
iex> Webern.retrograde(row)
%Webern.Row{
pitch_classes: [11, 9, 10, 8, 7, 5, 6, 4, 3, 1, 2, 0]
}
iex> Webern.retrograde(row, 4)
%Webern.Row{
pitch_classes: [3, 1, 2, 0, 11, 9, 10, 8, 7, 5, 6, 4]
}
See [above](#module-transforming-tone-rows) for details about
the retrograde form
"""
@spec retrograde(row, integer | nil) :: row
def retrograde(row = %Webern.Row{}, start \\ nil) do
with start <- start || List.first(row.pitch_classes) do
Row.retrograde(row, start)
end
end
@doc """
Returns the inverse form of `row`, optionally transposed
to begin at pitch class `start`.
iex> row = Webern.Row.new([0, 2, 1, 3, 4, 6, 5, 7, 8, 10, 9, 11])
iex> Webern.inverse(row)
%Webern.Row{
pitch_classes: [0, 10, 11, 9, 8, 6, 7, 5, 4, 2, 3, 1]
}
iex> Webern.inverse(row, 1)
%Webern.Row{
pitch_classes: [1, 11, 0, 10, 9, 7, 8, 6, 5, 3, 4, 2]
}
See [above](#module-transforming-tone-rows) for details about the inverse
form
"""
@spec inverse(row, integer | nil) :: row
def inverse(row = %Webern.Row{}, start \\ nil) do
with start <- start || List.first(row.pitch_classes) do
Row.inverse(row, start)
end
end
@doc """
Returns the retrograde inverse form of `row`, with the prime form optionally
transposed to begin at pitch class `start`.
## Example
iex> row = Webern.Row.new([0, 2, 1, 3, 4, 6, 5, 7, 8, 10, 9, 11])
iex> Webern.retrograde_inverse(row)
%Webern.Row{
pitch_classes: [1, 3, 2, 4, 5, 7, 6, 8, 9, 11, 10, 0]
}
iex> Webern.retrograde_inverse(row, 5)
%Webern.Row{
pitch_classes: [6, 8, 7, 9, 10, 0, 11, 1, 2, 4, 3, 5]
}
See [above](#module-transforming-tone-rows) for details about the retrograde
inverse form
"""
@spec retrograde_inverse(row, integer | nil) :: row
def retrograde_inverse(row = %Webern.Row{}, start \\ nil) do
with start <- start || List.first(row.pitch_classes) do
Row.retrograde_inverse(row, start)
end
end
@doc """
Returns the inverse retrograde form of `row`, with the prime form optionally
transposed to begin at pitch class `start`.
## Example
iex> row = Webern.Row.new([0, 2, 1, 3, 4, 6, 5, 7, 8, 10, 9, 11])
iex> Webern.inverse_retrograde(row)
%Webern.Row{
pitch_classes: [11, 1, 0, 2, 3, 5, 4, 6, 7, 9, 8, 10]
}
iex> Webern.inverse_retrograde(row, 8)
%Webern.Row{
pitch_classes: [7, 9, 8, 10, 11, 1, 0, 2, 3, 5, 4, 6]
}
See [above](#module-transforming-tone-rows) for details about the inverse
retrograde form
"""
@spec inverse_retrograde(row, integer | nil) :: row
def inverse_retrograde(row = %Webern.Row{}, start \\ nil) do
with start <- start || List.first(row.pitch_classes) do
Row.inverse_retrograde(row, start)
end
end
@doc """
Returns a tone row matrix using `row` as the initial prime form.
"""
@spec matrix(row) :: matrix
def matrix(row = %Webern.Row{}) do
Matrix.new(row)
end
@doc """
Generates lilypond and PDF files for the given row or matrix.
This function wraps `Webern.Lilypond.to_lily/1`, writing the content
that functions generates a lilypond file in your current working directory
and compiles it to a PDF file.
"""
@spec to_lily(row | matrix, String.t) :: charlist
def to_lily(object, filename) do
with full_filename <- filename <> ".ly",
:ok <- File.write(full_filename, Lilypond.to_lily(object))
do
"lilypond -o #{filename} #{full_filename}"
|> to_charlist |> :os.cmd
end
end
end