Skip to content

Commit

Permalink
Push down IntervalDescriptor to Music, leave only adapters (#121)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsbean authored Oct 14, 2018
1 parent a4bf47b commit 467160c
Show file tree
Hide file tree
Showing 20 changed files with 326 additions and 1,223 deletions.
8 changes: 4 additions & 4 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"repositoryURL": "https://github.com/dn-m/Music",
"state": {
"branch": null,
"revision": "7eae9d05fdca06a483ecf7e66869b8c96c690a25",
"version": "0.8.0"
"revision": "d4e19a8e1fce7be6417c6e346737c94f6ff3bbe8",
"version": "0.9.0"
}
},
{
Expand All @@ -33,8 +33,8 @@
"repositoryURL": "https://github.com/dn-m/Structure",
"state": {
"branch": null,
"revision": "2dd1fade2b78479c6500ef92b77b60e5021f9602",
"version": "0.19.0"
"revision": "ee95d59a077e28d956500a88b41ef9247f8ae0c6",
"version": "0.19.1"
}
}
]
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/dn-m/Structure", from: "0.19.0"),
.package(url: "https://github.com/dn-m/Math", from: "0.2.0"),
.package(url: "https://github.com/dn-m/Music", from: "0.5.0")
.package(url: "https://github.com/dn-m/Music", from: "0.9.0")
],
targets: [
// Sources
Expand Down
59 changes: 0 additions & 59 deletions Sources/SpelledPitch/CompoundSpelledInterval.swift

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// CompoundSpelledInterval.swift
// SpelledPitch
//
// Created by James Bean on 5/20/18.
//

import Math
import Algorithms
import Pitch

extension CompoundIntervalDescriptor {

/// Creates a `CompoundSpelledInterval` with the two given `SpelledPitch` values.
public init(_ a: SpelledPitch<EDO12>, _ b: SpelledPitch<EDO12>) {
self.init(OrderedIntervalDescriptor(a,b), displacedBy: abs(b.octave - a.octave))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// IntervalDescriptorExtensions.swift
// SpelledPitch
//
// Created by James Bean on 10/13/18.
//

import Pitch

extension IntervalDescriptor where Ordinal: SpelledPitchConvertingIntervalOrdinal {

/// Creates a `IntervalDescriptor`-conforming type with the given `interval` (i.e., the distance
/// between the `NoteNumber` representations of `Pitch` or `Pitch.Class` values) and the given
/// `steps` (i.e., the distance between the `LetterName` attributes of `Pitch.Spelling`
/// values).
init(interval: Double, steps: Int) {
let (quality,ordinal) = Self.qualityAndOrdinal(interval: interval, steps: steps)
self.init(quality,ordinal)
}

/// - Returns the `IntervalQuality` and `Ordinal` values for the given `interval` (i.e.,
/// the distance between the `NoteNumber` representations of `Pitch` or `Pitch.Class` values)
/// and the given `steps (i.e., the distance between the `LetterName` attributes of
///`Pitch.Spelling` values).
static func qualityAndOrdinal(interval: Double, steps: Int) -> (IntervalQuality, Ordinal) {
let distance = Ordinal.platonicDistance(from: interval, to: steps)
let ordinal = Ordinal(steps: steps)!
let quality = IntervalQuality(distance: distance, with: ordinal.platonicThreshold)
return (quality, ordinal)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// IntervalQualityExtensions.swift
// SpelledPitch
//
// Created by James Bean on 10/13/18.
//

import Pitch

extension IntervalQuality {
init(distance: Double, with platonicThreshold: Double) {
let (diminished, augmented) = (-platonicThreshold,platonicThreshold)
switch distance {
case diminished - 4:
self = .extended(.init(.quintuple, .diminished))
case diminished - 3:
self = .extended(.init(.quadruple, .diminished))
case diminished - 2:
self = .extended(.init(.triple, .diminished))
case diminished - 1:
self = .extended(.init(.double, .diminished))
case diminished:
self = .extended(.init(.single, .diminished))
case -0.5:
self = .imperfect(.minor)
case +0.0:
self = .perfect(.perfect)
case +0.5:
self = .imperfect(.major)
case augmented:
self = .extended(.init(.single, .augmented))
case augmented + 1:
self = .extended(.init(.double, .augmented))
case augmented + 2:
self = .extended(.init(.triple, .augmented))
case augmented + 3:
self = .extended(.init(.quadruple, .augmented))
case augmented + 4:
self = .extended(.init(.quintuple, .augmented))
default:
fatalError("Not possible to create a NamedIntervalQuality with interval \(distance)")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// OrderedIntervalDescriptorExtensions.swift
// SpelledPitch
//
// Created by James Bean on 1/8/17.
//
//

import Math
import Algorithms
import Pitch

extension OrderedIntervalDescriptor {

/// Creates an `OrderedIntervalDescriptor` with two `SpelledPitch` values.
public init(_ a: SpelledPitch<EDO12>, _ b: SpelledPitch<EDO12>) {
let (a,b,didSwap) = swapped(a,b) { a > b }
let (interval,steps) = intervalAndSteps(a,b)
let (quality,ordinal) = OrderedIntervalDescriptor.qualityAndOrdinal(interval: interval, steps: steps)
self.init(didSwap ? .descending : .ascending, ordinal, quality)
}
}

extension OrderedIntervalDescriptor.Ordinal: SpelledPitchConvertingIntervalOrdinal {

/// - Returns: The distance in semitones from an iedal interval at which point an interval
/// quality becomes diminished or augmented for a given `Ordinal`.
var platonicThreshold: Double {
switch self {
case .perfect:
return 1
case .imperfect:
return 1.5
}
}

/// - Returns: The "ideal" interval for the given amount of scalar steps between two
/// `SpelledPitch` values.
///
/// For example, perfect intervals have a single ideal spelling, whereas imperfect intervals
/// could be spelled two different ways.
static func platonicInterval(steps: Int) -> Double {
assert((0..<7).contains(steps))
switch steps {
case 0:
return 0
case 1:
return 1.5
case 2:
return 3.5
case 3:
return 5
case 4:
return 7
case 5:
return 8.5
case 6:
return 10.5
default:
fatalError("Impossible")
}
}
}

private func intervalAndSteps(_ a: SpelledPitch<EDO12>, _ b: SpelledPitch<EDO12>) -> (Double,Int) {
return ((interval(a,b), steps(a,b)))
}

private func interval(_ a: SpelledPitch<EDO12>, _ b: SpelledPitch<EDO12>) -> Double {
return (b.pitch - a.pitch).value.value
}

private func steps(_ a: SpelledPitch<EDO12>, _ b: SpelledPitch<EDO12>) -> Int {
return mod(b.spelling.letterName.steps - a.spelling.letterName.steps, 7)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//
// UnorderedSpelledInterval.swift
// SpelledPitch
//
// Created by James Bean on 1/8/17.
//
//

import Algorithms
import Math
import Pitch

extension UnorderedIntervalDescriptor {

/// Creates a `UnorderedSpelledInterval` with two `SpelledPitch` values.
public init(_ a: Pitch.Spelling<EDO12>, _ b: Pitch.Spelling<EDO12>) {
let (a,b) = ordered(a,b)
let (interval, steps) = intervalAndSteps(a,b)
self.init(interval: interval, steps: steps)
}
}

/// - Returns: The two `Pitch.Spelling` values such that the difference between `b` and `a` is less
/// that the difference between `a` and `b`.
private func ordered (_ a: Pitch.Spelling<EDO12>, _ b: Pitch.Spelling<EDO12>)
-> (Pitch.Spelling<EDO12>,Pitch.Spelling<EDO12>)
{
let (a,b,_) = swapped(a, b) { mod(steps(a,b), 7) > mod(steps(b,a), 7) }
return (a,b)
}

/// - Returns: The steps and interval between the two given `Pitch.Spelling` values.
private func intervalAndSteps(_ a: Pitch.Spelling<EDO12>, _ b: Pitch.Spelling<EDO12>) -> (Double,Int) {
return (interval(a,b), steps(a,b))
}

/// - Returns: The amount of semitones between the two given `Pitch.Spelling` values.
private func interval(_ a: Pitch.Spelling<EDO12>, _ b: Pitch.Spelling<EDO12>) -> Double {
return OrderedInterval(a.pitchClass,b.pitchClass).value.value
}

/// - Returns: The amount of steps between the two given `Pitch.Spelling` values.
private func steps(_ a: Pitch.Spelling<EDO12>, _ b: Pitch.Spelling<EDO12>) -> Int {
return mod(b.letterName.steps - a.letterName.steps, 7)
}

extension UnorderedIntervalDescriptor.Ordinal: SpelledPitchConvertingIntervalOrdinal {

/// - Returns: The distance in semitones from an iedal interval at which point an interval
/// quality becomes diminished or augmented for a given `Ordinal`.
public var platonicThreshold: Double {
switch self {
case .perfect:
return 1
case .imperfect:
return 1.5
}
}

/// - Returns: The "ideal" interval for the given amount of scalar steps between two
/// `SpelledPitch` values.
///
/// For example, perfect intervals have a single ideal spelling, whereas imperfect intervals
/// could be spelled two different ways.
static func platonicInterval(steps: Int) -> Double {
assert((0..<4).contains(steps))
switch steps {
case 0: // unison
return 0
case 1: // second
return 1.5
case 2: // third
return 3.5
case 3: // fourth
return 5
default: // impossible
fatalError("Impossible")
}
}
}
Loading

0 comments on commit 467160c

Please sign in to comment.