Git Product home page Git Product logo

music's Introduction

dn-m

Application targets for the dn-m project.

music's People

Contributors

jsbean avatar mossheim avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

music's Issues

[Model] Store and retrieve entities by type

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]
    }
}

Ideas about global storage

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)
    }
}

Conditionally import Glibc instead of assuming Darwin

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.

Rename MetricalContext

The currently named MetricalContext represents the state of a single

Some candidates:

  • RhythmContext
  • MetricalItem
  • MetricalEvent
  • MetricalElement
  • ...

[Pitch] Let Pitch store center pitch and scale-factor (fractional) / interval

@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 !

[Duration] Implement more type-safe Rhythm.Leaf.Kind.Collection and Builder

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 
{
    ...
}

Remove Duration runtime constructor errors

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.

[Pitch] Add Scale

cc @bwetherfield

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
}

[Dynamics] Consider using phantom types to statically enforce well-formedness

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]
}

[Pitch] Add Chord

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 { ... }
}

[Meter] Add additive meter

Example usage:

let meter = AdditiveMeter(Meter(1,4), FractionalMeter(Fraction(2,3),4)])
//  1 + 2/3
//  4    4

[README] Add higher-level usage

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).

[Duration] ProportionTree / DurationTree

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.

Redesign Articulation in final tagless style

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 {
    ...
}

[Pitch] Consider building out ScaleDegree structure

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.

[Pitch] Add Chord descriptor

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)

[Pitch] Add Pitch.init(_: Pitch.Class)

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)
    }
}

Relax denominator constraint for Meter

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).

Rename Meter.Context.meter to meterFragment

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.

[Duration/Meter] Audit Additive conformance of 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.

[Meter] Lift Meter to protocol

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>    
    }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.