Duration
The Duration
module defines basic structures for describing a symbolic representation of time by hierarchical subdivision. The building blocks of the Duration
module are the Duration
, Rhythm
, Tempo
, and Meter
structures.
Duration
The Duration
structure is a Rational
type, defined by its numerator (Beats
) over its power-of-two denominator (Subdivision
). A Duration
can be instantiated with a Beats
and Subdivision
.
let dur = Duration(5,32)
Warning: The denominator must be a power-of-two, otherwise your program will crash.
let notDur = Duration(4,17) // boom
A Duration
can be created with a shorthand via the />
operator.
let dur = 17/>64
Rhythm
ProportionTree
A ProportionTree
is a typealias
for Tree<Int,Int>
, from the dn-m/Structure/DataStructures
module, which provides a hierarchical model of relative durational values without a specificed Duration
container size. A ProportionTree
can be as simple as a pair of eighth notes,
let eighthPair: ProportionTree = .branch(1, [1,1])
or as complicated as a deeply nested tuplet.
let nested: ProportionTree = .branch(1, [
.branch(2, [
.branch(2, [
.leaf(1),
.leaf(1)
]),
.leaf(3)
]),
.branch(4, [
.leaf(3),
.leaf(4),
.branch(6, [
.leaf(1),
.leaf(1)
]),
.leaf(2),
.branch(2, [
.leaf(3),
.leaf(5)
])
]),
.branch(3, [
.leaf(2),
.leaf(4),
.branch(1, [
.leaf(16),
.leaf(17)
])
])
])
Note: This type is useful for composing up arbitrarily simple or complex rhythms without having to commit to an overall
Duration
, or having to normalize the subdivision relationships between all of the nodes.
DurationTree
A DurationTree
is a typealias
for Tree<Duration,Duration>
, from the dn-m/Structure/DataStructures
module.
A DurationTree
is often not needed to be created by user, but is often the composition of a Duration
container size and a ProportionTree
.
let proportions: ProportionTree = .branch(1, [1,2,4])
let duration = 13/>64
let durationTree = DurationTree(duration, proportions)
The beat and subdivision values of each node of the resulting DurationTree
will be normalized automatically
durationTree // => .branch(13/>64, [
.leaf(2/>64),
.leaf(4/>64),
.leaf(8/>64)
])
for a resulting tuplet ratio of 14:13
.
Rhythm.Leaf
A Rhythm.Leaf
models the metrical identity of a given rhythmic leaf item. It is a typealias
for ContinuationOrInstance<AbsenceOrEvent<Element>>
.
.continuation
(i.e., a leaf istied
over from the previous event).instance(.rest)
(i.e., a leaf is arest
, a measured silence).instance(.event(Element))
(i.e., a leaf is a measured non-silence)
ContinuationOrInstance
ContinuationOrInstance
is a two-case generic enum similar to the Optional
enum of the Standard Library, which describes whether a rhythmic item is:
- a
.continuation
(i.e.,tied-over
from its preceeding item), or - an
.instance(Element)
(i.e., an action occuring).
AbsenceOrEvent
AbsenceOrEvent
is a two-case generic enum similar to the Optional
enum of the Standard Library, which describes whether a rhythmic item is:
- an
.absence
(i.e., arest
), or - an
.event(Element)
(i.e., anote
).
Rhythm
A Rhythm
is the composition of a DurationTree
and an array of Rhythm.Leaf
values (with a length equivalent to the leaves of the durationTree
). The Rhythm.Leaf
values can store any value generically.
We can decorate the durationTree
above like so:
let rhythm = Rhythm<String>(durationTree, [
.instance(.absence), // rest
.instance(.event("BANG")), // start event
.continuation // tied from previous event
])
Meter
Like a Duration
, a Meter
is a Rational
type, used for defining the metrical context of measure.
You can create a common time
meter,
let meter = Meter(4,4)
or maybe something a little more fun.
let meter = Meter(31,128)
Tempo
A Tempo
is the definition of a pulse occurring at a given frequency (beatsPerMinute
) at a given Subdivision
level (e.g., quarter, sixteenth, etc.).
let stayinAlive = Tempo(100, subdivision: 4) // remember this for CPR
Tempo.Interpolation.Easing
A Tempo.Interpolation.Easing
specifies the curve of a Tempo.Interpolation
.
linear
powerIn
powerInOut
exponentialIn
sineInOut
Tempo.Interpolation
A Tempo.Interpolation
defines a transition between two Tempo
values, over a given length
, with a specified easing
.
let startTempo = Tempo(24, subdivision: 4)
let endTempo = Tempo(192, subdivision: 8)
let interpolation = Tempo.Interpolation(
start: startTempo,
end: endTempo,
length: Fraction(37,4),
easing: .sineInOut
)
Note: Subdivision values needn’t be the same.
Tempo.Interpolation.Fragment
A Tempo.Interpolation.Fragment
is a portion of a Tempo.Interpolation
within a given range
.
let startTempo = Tempo(24)
let endTempo = Tempo(72)
let interpolation = Tempo.Interpolation(
start: startTempo,
end: endTempo,
length: Fraction(13,4)
)
let fragment = Tempo.Interpolation.Fragment(
interpolation,
in: Fraction(5,4) ..< Fraction(11,4)
)
Note: The default
subdivision
value is4
and the defaulteasing
value is.linear
.
Tempo.Interpolation.Collection
A Tempo.Interpolation.Collection
defines a contiguous collection of Tempo.Interpolation.Fragment
values, which are indexed by the offset Fraction
value.
Tempo.Interpolation.Collection.Builder
A helper class Tempo.Interpolation.Collection.Builder
is provided to encapsulate the stateful construction process of a Tempo.Interpolation.Collection
.
You can start with an empty Builder
,
let builder = Tempo.Interpolation.Collection.Builder()
create a few interpolations (e.g., accelerando, ritardando, static),
let a = Tempo.Interpolation(
start: Tempo(60),
end: Tempo(120),
length: Fraction(4,4)
)
let b = Tempo.Interpolation(
start: Tempo(120),
end: Tempo(60),
length: Fraction(4,4)
)
let c = Tempo.Interpolation(
start: Tempo(60),
end: Tempo(60),
length: Fraction(4,4)
)
then add them with the Builder
.
for interpolation in [a,b,c] {
builder.add(interpoation)
}
Now we are ready for the Builder
to give us a finished product.
let collection = builder.build()
If it makes more sense, we can also create a Tempo.Interpolation.Collection
directly.
let collection = Tempo.Interpolation.Collection([a,b,c])
In the case that we need to retrieve a subsection of your collection
, we can provide a subscript with a Range<Fraction>
to get it out.
let fragment = collection[Fraction(3,32)..<Fraction(19,8)]
This will produce a Tempo.Interpolation.Collection
with three Tempo.Interpolation.Fragments
representing the three subsections of the interpolations we created above.