Application targets for the dn-m project.
dn-m / music Goto Github PK
View Code? Open in Web Editor NEWStructures for the creation, analysis, and performance of music in Swift
License: MIT License
Structures for the creation, analysis, and performance of music in Swift
License: MIT License
When conditional conformances
is implemented, only make theEquatable
conformance if T: Equatable
.
A little bit of a work around needs to be made In order to keep the model type-safe:
extension Model {
var entitiesByType: [String: [Identifier]] = [:]
func store <T> (_ value: T, ...) -> Identifier {
let identifier = // make new identifier
entitiesByType.safelyAppend(value, forKey: "\(T.self)")
return identifier
}
func retrieve <T> (_ identifier: Identifier) -> T {
return entities["\(T.self)"] as! [T]
}
}
I wonder if the following could be of use, @jsbean ?
struct Prism <A,B> {
let unwrap: (A) -> B?
let wrap: (B) -> A
}
enum Music {
static var objects: [Music] = []
case duration(Duration)
case pitch(Pitch)
...
}
extension Music {
enum prism {
static let duration = Prism<Music, Duration>(
unwrap: {
if case let .duration(object) = $0 { return object }
return nil
},
wrap: Music.duration
)
static let pitch = Prism<Music, Pitch>(
unwrap: {
if case let .pitch(object) = $0 { return object }
return nil
},
wrap: Music.pitch
)
}
...
}
extension Music {
func allDurations() -> [Duration] {
return Music.objects.compactMap(Music.prism.duration.unwrap)
}
func allPitches() -> [Pitch] {
return Music.objects.compactMap(Music.prism.pitch.unwrap)
}
}
Example usage:
let meter = FractionalMeter(Fraction(2,3),4)
As an analogy to Scale.Degree
(#91), add the Chord.Factor
structure for managing component parts of chords.
Conform Chord
to RandomAccessCollectionWrapping
.
.middleC
exposes information that is tied up with Western musical notation, and should be removed from Pitch
.
There are several instances where Darwin
is imported:
-Sources/Pitch/NoteNumber.swift
-Sources/Pitch/Frequency.swift
-Sources/Pitch/UnorderedInterval.swift
-Sources/Tempo/Tempo.swift
-Sources/Rhythm/ProportionTree.swift
In order to serve Linux, conditionally import Glibc
.
The currently named MetricalContext
represents the state of a single
Some candidates:
RhythmContext
MetricalItem
MetricalEvent
MetricalElement
...
@jsbean, I wonder if you have thoughts on this, or how to put it into practice.
At its most basic, I am imagining something like this:
public struct Pitch: NoteNumberRepresentable {
// MARK: - Instance Properties
/// The `NoteNumber` representation of this `Pitch`.
public let value: NoteNumber
/// The `scaleFactor` by which this pitch is scaled
public let scaleFactor: Fraction
// MARK: - Initializers
/// Creates a `Pitch` with the given `NoteNumber` value.
public init(_ value: NoteNumber, _ scaleFactor: Fraction = Fraction.one) {
self.value = value
self.scaleFactor = scaleFactor
}
}
Alternatively, the Fraction
could be an interval - the rational case, is the same as using some just interval... I'm not too sure how the interval ecosystem works !
There is currently no way of ensuring that a .continuation(...)
does not succeed an .absence
.
Perhaps a good way to deal with this is similar to the Path.Builder
.
extension Rhythm.Leaf.Kind {
struct Collection { }
}
protocol AllowingStart {
func start(_ attack: Element) -> AllowingStart & AllowingContinuation & AllowingAbsence
func start(_ attack: Element, _ release: Element) -> AllowingStart & AllowingAbsence
}
protocol AllowingContinuation {
func continuation(_ sustain: Element) -> AllowingStart & AllowingContinuation & AllowingAbsence
// if a release element is provided, don't allow continuation
func continuation(_ sustain: Element, _ release: Element) -> AllowingStart & AllowingAbsence
}
protocol AllowingAbsence {
// don't allow continuation after rest
func absence() -> AllowingStart & AllowingAbsence
}
extension Rhythm.Leaf.Kind.Collection {
final class Builder {
var storage: [Leaf.Kind] = []
}
}
extension Rhythm.Leaf.Kind.Collection.Builder:
AllowingStart,
AllowingContinuation,
AllowingAbsence
{
...
}
At present let badDur: Duration = 1/>31
causes an error, because 31 is not a power of 2.
Proposed eradication of these errors: A constructor of the following form.
init(_ numerator: Beats, _ power: Power) {
self.beats = numerator
self.denominator = pow(2, power)
}
where Power
may be implemented as Int
as Subdivision
is now.
Add the Scale
struct. Similarly to the OrderedInterval.Collection
/Chord
pairing here, there is an abstract form and a concrete form. In fact, the underlying structure may be identical to that of the Chord
universe, but with different affordances.
struct Scale {
let intervals: OrderedInterval.Collection
let first: Pitch
}
Given a Rhythm
, take any arbitrary Fraction
range from it.
Currently, any order of Dynamic.Element
values can be shoved into a Dynamic
. However, there is a coherent grammar for Dynamic
values (mf
, mp
, ppp
, sfz
, and o
are valid, while mfff
and spppfz
are not). Such a grammar is enforced dynamically in the Dynamic.Validator
, which must look over each element.
Instead, we can look into injecting a phantom type into the Dynamic
to encode this grammar into the type system statically at compile time. This will enforce well-formedness, and reduce wasted CPU cycles in validation during runtime.
For example:
protocol DynamicElementState { }
enum P: DynamicElementState { }
enum F: DynamicElementState { }
enum M: DynamicElementState { }
enum O: DynamicElementState { }
struct Dynamic {
let elements: [Element]
}
The Model.Filter
is a structure that breaks down a Model
along three axes: performing forces, interval of musical time, and type of attribute.
cc @bwetherfield (and @brianlheim if interested).
Add Chord
functionality to the Pitch
module. The Chord
structure would be an abstract musical concept (i.e., no knowledge of SpelledPitch
).
Perhaps there could be two types which are related: OrderedInterval.Collection
,
extension OrderedInterval {
struct Collection {
let intervals: [OrderedInterval]
}
}
and Chord
.
struct Chord {
let first: Pitch
let intervals: OrderedInterval.Collection
init(intervals: OrderedInterval.Collection, at first: Pitch) { ... }
init <S> (_ sequence: S) where S: Sequence, S.Element == Pitch { ... }
}
Use dn-m/Structure/permutations.
Example usage:
let meter = AdditiveMeter(Meter(1,4), FractionalMeter(Fraction(2,3),4)])
// 1 + 2/3
// 4 4
When IntervalSearchTree
is established and reasonably vetted, push it down to DataStructures
.
Currently, the README shows low-level usage for each module.
Instead, show a hello, world
high-level usage for the entire package, and add links to documentation for each module (which show respective low-level usage).
Implement ProportionTree
/ DurationTree
with COW structs holding final class
nodes. While the enum
-based Tree
implementation was elegant, performance issues are emerging.
By adding the @inlinable
attribute to these enum
-based ADT operations, things do get considerably better, but it will probably not be enough.
Notated with "feather" beams, but not merely a notational phenomenon.
Some ideas for replacing the enum based articulation with a final tagless approach. This will enable more user customization downstream.
// Articulation that can be represented by a single symbol above or below a notehead
protocol SymbolizableArticulation {
static func staccato() -> Self
static func staccatissimo() -> Self
static func marcato() -> Self
...
static func stack([Self]) -> Self
}
typealias DrawnMarking = SomeView // for example - something drawn
typealias MarkingDescribed = String // string representation like "staccato"
extension DrawnMarking: SymbolizableArticulation {
...
}
extension MarkingDesribed: SymbolisableArticulation {
...
}
Build out a ScaleDegree
structure, with different representational affordances. The ScaleDegree
might have an index
(0
, 1
...), and depending on the context of the Scale
(major, minor, etc.) it may be represented in different ways (I
or i
, etc.).
Such a structure could also encapsulate the logic of representing diminished / half-diminished, aug 6, sharp-5-flat-9 extensions.
The IntervalDescriptor
analog to pitch for Chord
values.
struct ChordDescriptor {
let intervals: [CompoundIntervalDescriptor]
init(_ intervals: [CompoundIntervalDescriptor]) { ... }
}
This will make it easy to create this down the line:
let chord: ChordDescriptor = [.init(.major, .third), .init(.minor, .third)]
let spelledChord = SpelledChord(.middleC, chord)
In order to create a Pitch
from a Pitch.Class
, you currently have to dig down to the pitch class double value
.
Instead, add a convenience initializer:
extension Pitch {
init(_ class: Pitch.Class) {
self.init(class.value)
}
}
Currently, Meter
requires its denominator to be a power-of-two (with a coefficient of 2), just like a Duration
.
However, the coefficient should be relaxed to be a power-of-two with any coefficient, so as to allow Meter(5,6)
or (31,28)
.
The actual type is Meter.Fragment
. At the call site, meterContext.meter.meter
is currently required, which smells really bad.
Instead, it shall be: meterContext.meterFragment.meter
.
Meter
and Meter.Kind
are both currently Additive
(and Multiplicative
). Because a Meter.Kind
can be any of { .single
, .fractional
, or .additive
}, the +
operator make returns an .additive
sum of the two given Meter
values.
This is interesting on a small scale, but blows up (showstoppingly) on a large scale. For example,
Meter(3,4) + Meter(2,4) // => Meter(kind: .additive([.single(3,4), .single(2,4)]))
This keeps building out a larger and larger .additive
Meter
which must keep allocating arrays for each new pair.
Consider switching on the kinds
of the incoming Meter
values for +
. If they are both .single
, return a .single
. This could potentially save a lot of computation.
Currently, there is a Meter
structure, which should expand to allow #52 and #53.
Lift Meter
to a protocol:
protocol MeterProtocol: DurationSpanning, Fragmentable where Fragment: Meter {
var length: Fraction
var beatOffsets: [Fraction]
}
Then implement the current Meter
accordingly:
struct Meter: MeterProtocol {
var length: Fraction { ... }
var beatOffsets: [Fraction] { ... }
}
This will leave room to implement AdditiveMeter
and FractionalMeter
:
extension Meter {
struct Additive: MeterProtocol {
...
}
struct Fractional: MeterProtocol {
...
}
}
This will be critical for Meter.Collection
to glide along smoothly:
extension Meter {
struct Collection <MeterType: MeterProtocol> {
let base: SortedDictionary<Fraction,MeterProtocol.Fragment>
}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.