From 08b07a053c6aae8a744d2440a1621e8823f314c2 Mon Sep 17 00:00:00 2001 From: Cem Olcay Date: Sat, 13 Jul 2024 11:43:41 +0300 Subject: [PATCH 1/8] Update note value type --- MusicTheory.xcodeproj/project.pbxproj | 104 +++++++++++++++--- .../xcschemes/MusicTheory Mac.xcscheme | 2 +- .../xcschemes/MusicTheory TV.xcscheme | 2 +- .../xcschemes/MusicTheory Watch.xcscheme | 2 +- .../xcschemes/MusicTheory iOS.xcscheme | 2 +- .../xcschemes/MusicTheoryTests.xcscheme | 2 +- Playground.playground/contents.xcplayground | 2 +- Sources/MusicTheory/NoteValue.swift | 79 +++++-------- Sources/MusicTheory/Tempo.swift | 4 +- Sources/MusicTheory/TimeSignature.swift | 2 +- Tests/MusicTheoryTests/MusicTheoryTests.swift | 2 +- 11 files changed, 123 insertions(+), 80 deletions(-) diff --git a/MusicTheory.xcodeproj/project.pbxproj b/MusicTheory.xcodeproj/project.pbxproj index 43ae102..aadd044 100644 --- a/MusicTheory.xcodeproj/project.pbxproj +++ b/MusicTheory.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -349,8 +349,9 @@ B22DCF381E1590E7006D561E /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 1220; + LastUpgradeCheck = 1540; ORGANIZATIONNAME = cemolcay; TargetAttributes = { B219CC011E4CFB870060A6B6 = { @@ -551,10 +552,17 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/MusicTheory/Info-iOS.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; PRODUCT_BUNDLE_IDENTIFIER = "co.prototapp.MusicTheory-iOS"; PRODUCT_NAME = MusicTheory; SKIP_INSTALL = YES; @@ -574,10 +582,17 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/MusicTheory/Info-iOS.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; PRODUCT_BUNDLE_IDENTIFIER = "co.prototapp.MusicTheory-iOS"; PRODUCT_NAME = MusicTheory; SKIP_INSTALL = YES; @@ -598,9 +613,16 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/MusicTheory/Info-Watch.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; PRODUCT_BUNDLE_IDENTIFIER = "co.prototapp.MusicTheory-Watch"; PRODUCT_NAME = MusicTheory; SDKROOT = watchos; @@ -624,9 +646,16 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/MusicTheory/Info-Watch.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; PRODUCT_BUNDLE_IDENTIFIER = "co.prototapp.MusicTheory-Watch"; PRODUCT_NAME = MusicTheory; SDKROOT = watchos; @@ -649,9 +678,16 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/MusicTheory/Info-TV.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; PRODUCT_BUNDLE_IDENTIFIER = "co.prototapp.MusicTheory-TV"; PRODUCT_NAME = MusicTheory; SDKROOT = appletvos; @@ -674,9 +710,16 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/MusicTheory/Info-TV.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; PRODUCT_BUNDLE_IDENTIFIER = "co.prototapp.MusicTheory-TV"; PRODUCT_NAME = MusicTheory; SDKROOT = appletvos; @@ -695,16 +738,23 @@ buildSettings = { CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 77Y3N48SNF; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; FRAMEWORK_VERSION = A; INFOPLIST_FILE = "$(SRCROOT)/Sources/MusicTheory/Info-Mac.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.9; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; PRODUCT_BUNDLE_IDENTIFIER = "co.prototapp.MusicTheory-Mac"; PRODUCT_NAME = MusicTheory; SDKROOT = macosx; @@ -719,16 +769,23 @@ buildSettings = { CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 77Y3N48SNF; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; FRAMEWORK_VERSION = A; INFOPLIST_FILE = "$(SRCROOT)/Sources/MusicTheory/Info-Mac.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.9; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; PRODUCT_BUNDLE_IDENTIFIER = "co.prototapp.MusicTheory-Mac"; PRODUCT_NAME = MusicTheory; SDKROOT = macosx; @@ -775,6 +832,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -842,6 +900,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -855,7 +914,8 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -871,10 +931,15 @@ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 77Y3N48SNF; INFOPLIST_FILE = Tests/MusicTheoryTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.12; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = com.cemolcay.MusicTheoryTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; @@ -888,10 +953,15 @@ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 77Y3N48SNF; INFOPLIST_FILE = Tests/MusicTheoryTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.12; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; PRODUCT_BUNDLE_IDENTIFIER = com.cemolcay.MusicTheoryTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; diff --git a/MusicTheory.xcodeproj/xcshareddata/xcschemes/MusicTheory Mac.xcscheme b/MusicTheory.xcodeproj/xcshareddata/xcschemes/MusicTheory Mac.xcscheme index 9a5f2f7..2ecbf5d 100644 --- a/MusicTheory.xcodeproj/xcshareddata/xcschemes/MusicTheory Mac.xcscheme +++ b/MusicTheory.xcodeproj/xcshareddata/xcschemes/MusicTheory Mac.xcscheme @@ -1,6 +1,6 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/Sources/MusicTheory/NoteValue.swift b/Sources/MusicTheory/NoteValue.swift index 6d39e16..08763df 100644 --- a/Sources/MusicTheory/NoteValue.swift +++ b/Sources/MusicTheory/NoteValue.swift @@ -13,71 +13,44 @@ import Foundation // MARK: - NoteValueType /// Defines the types of note values. -public enum NoteValueType: Int, Codable, CaseIterable, Hashable, CustomStringConvertible { +public struct NoteValueType: Codable, Hashable, CustomStringConvertible { + /// Note value in beats. + public var beats: Double + /// Name of the note value. + public var description: String + + /// Sixteen bar notes. - case sixteenBars + public static let sixteenBars = NoteValueType(beats: 16.0, description: "16 Bars") /// Eigth bar notes. - case eigthBars + public static let eigthBars = NoteValueType(beats: 8.0, description: "8 Bars") /// Four bar notes. - case fourBars + public static let fourBars = NoteValueType(beats: 4.0, description: "4 Bars") /// Two bar notes. - case twoBars + public static let twoBars = NoteValueType(beats: 2.0, description: "2 Bars") /// One bar note. - case oneBar + public static let oneBar = NoteValueType(beats: 1.0, description: "1 Bar") /// Two whole notes. - case doubleWhole + public static let doubleWhole = NoteValueType(beats: 2.0 / 1.0, description: "2/1") /// Whole note. - case whole + public static let whole = NoteValueType(beats: 1.0 / 1.0, description: "1/1") /// Half note. - case half + public static let half = NoteValueType(beats: 1.0 / 2.0, description: "1/2") /// Quarter note. - case quarter + public static let quarter = NoteValueType(beats: 1.0 / 4.0, description: "1/4") /// Eighth note. - case eighth + public static let eighth = NoteValueType(beats: 1.0 / 8.0, description: "1/8") /// Sixteenth note. - case sixteenth + public static let sixteenth = NoteValueType(beats: 1.0 / 16.0, description: "1/16") /// Thirtysecond note. - case thirtysecond + public static let thirtysecond = NoteValueType(beats: 1.0 / 32.0, description: "1/32") /// Sixtyfourth note. - case sixtyfourth - - /// The note value's duration in beats. - public var rate: Double { - switch self { - case .sixteenBars: return 16.0 / 1.0 - case .eigthBars: return 8.0 / 1.0 - case .fourBars: return 4.0 / 1.0 - case .twoBars: return 2.0 / 1.0 - case .oneBar: return 1.0 / 1.0 - case .doubleWhole: return 2.0 / 1.0 - case .whole: return 1.0 / 1.0 - case .half: return 1.0 / 2.0 - case .quarter: return 1.0 / 4.0 - case .eighth: return 1.0 / 8.0 - case .sixteenth: return 1.0 / 16.0 - case .thirtysecond: return 1.0 / 32.0 - case .sixtyfourth: return 1.0 / 64.0 - } - } - - /// Returns the string representation of the note value type. - public var description: String { - switch self { - case .sixteenBars: return "16 Bars" - case .eigthBars: return "8 Bars" - case .fourBars: return "4 Bars" - case .twoBars: return "2 Bars" - case .oneBar: return "1 Bar" - case .doubleWhole: return "2/1" - case .whole: return "1/1" - case .half: return "1/2" - case .quarter: return "1/4" - case .eighth: return "1/8" - case .sixteenth: return "1/16" - case .thirtysecond: return "1/32" - case .sixtyfourth: return "1/64" - } - } + public static let sixtyfourth = NoteValueType(beats: 1.0 / 64.0, description: "1/64") + + public static let all: [NoteValueType] = [ + .sixteenBars, .eigthBars, .fourBars, .twoBars, .oneBar, + .half, .quarter, .eighth, .sixteenth, .thirtysecond, .sixtyfourth + ] } // MARK: - NoteModifier @@ -113,7 +86,7 @@ public enum NoteModifier: Double, Codable, CaseIterable, CustomStringConvertible /// - noteValueType: The note value type to measure the length of the note value. /// - Returns: Returns how many notes of a single `NoteValueType` is equivalent to a given `NoteValue`. public func / (noteValue: NoteValue, noteValueType: NoteValueType) -> Double { - return (noteValue.type.rate * noteValue.modifier.rawValue) / noteValueType.rate + return (noteValue.type.beats * noteValue.modifier.rawValue) / noteValueType.beats } /// Defines the duration of a note beatwise. diff --git a/Sources/MusicTheory/Tempo.swift b/Sources/MusicTheory/Tempo.swift index dd80cbd..f365468 100644 --- a/Sources/MusicTheory/Tempo.swift +++ b/Sources/MusicTheory/Tempo.swift @@ -30,7 +30,7 @@ public struct Tempo: Codable, Hashable, CustomStringConvertible { /// Caluclates the duration of a note value in seconds. public func duration(of noteValue: NoteValue) -> TimeInterval { let secondsPerBeat = 60.0 / bpm - return secondsPerBeat * (timeSignature.noteValue.rate / noteValue.type.rate) * noteValue.modifier.rawValue + return secondsPerBeat * (timeSignature.noteValue.beats / noteValue.type.beats) * noteValue.modifier.rawValue } /// Calculates the note length in samples. Useful for sequencing notes sample accurate in the DSP. @@ -41,7 +41,7 @@ public struct Tempo: Codable, Hashable, CustomStringConvertible { /// - Returns: Returns the sample length of a note value. public func sampleLength(of noteValue: NoteValue, sampleRate: Double = 44100.0) -> Double { let secondsPerBeat = 60.0 / bpm - return secondsPerBeat * sampleRate * ((Double(timeSignature.beats) * noteValue.type.rate) * noteValue.modifier.rawValue) + return secondsPerBeat * sampleRate * ((Double(timeSignature.beats) * noteValue.type.beats) * noteValue.modifier.rawValue) } /// Calculates the LFO speed of a note vaule in hertz. diff --git a/Sources/MusicTheory/TimeSignature.swift b/Sources/MusicTheory/TimeSignature.swift index aa4ce55..4574623 100644 --- a/Sources/MusicTheory/TimeSignature.swift +++ b/Sources/MusicTheory/TimeSignature.swift @@ -30,6 +30,6 @@ public struct TimeSignature: Codable, Hashable, CustomStringConvertible { // MARK: CustomStringConvertible public var description: String { - return "\(beats)/\(Int(noteValue.rate))" + return "\(beats)/\(Int(noteValue.beats))" } } diff --git a/Tests/MusicTheoryTests/MusicTheoryTests.swift b/Tests/MusicTheoryTests/MusicTheoryTests.swift index 0edf8d3..4372eb4 100644 --- a/Tests/MusicTheoryTests/MusicTheoryTests.swift +++ b/Tests/MusicTheoryTests/MusicTheoryTests.swift @@ -6,7 +6,7 @@ // Copyright © 2016 prototapp. All rights reserved. // -import MusicTheory +@testable import MusicTheory import XCTest class MusicTheoryTests: XCTestCase { From 5881d6e0c9fd35f6bf940749b967000df8d1cc50 Mon Sep 17 00:00:00 2001 From: Cem Olcay Date: Sat, 13 Jul 2024 11:44:54 +0300 Subject: [PATCH 2/8] Fix indentation --- Sources/MusicTheory/Accidental.swift | 244 +-- Sources/MusicTheory/Chord.swift | 1454 ++++++++--------- Sources/MusicTheory/ChordProgression.swift | 392 ++--- Sources/MusicTheory/HarmonicFunctions.swift | 144 +- Sources/MusicTheory/Interval.swift | 400 ++--- Sources/MusicTheory/Key.swift | 410 ++--- Sources/MusicTheory/NoteValue.swift | 132 +- Sources/MusicTheory/Pitch.swift | 390 ++--- Sources/MusicTheory/Scale.swift | 236 +-- Sources/MusicTheory/ScaleType.swift | 1016 ++++++------ Sources/MusicTheory/Tempo.swift | 106 +- Sources/MusicTheory/TimeSignature.swift | 40 +- Tests/MusicTheoryTests/MusicTheoryTests.swift | 690 ++++---- 13 files changed, 2827 insertions(+), 2827 deletions(-) diff --git a/Sources/MusicTheory/Accidental.swift b/Sources/MusicTheory/Accidental.swift index de40fdf..0be4681 100644 --- a/Sources/MusicTheory/Accidental.swift +++ b/Sources/MusicTheory/Accidental.swift @@ -17,7 +17,7 @@ import Foundation /// - rhs: Right hand side of the equation. /// - Returns: Returns the sum of two accidentals. public func + (lhs: Accidental, rhs: Accidental) -> Accidental { - return Accidental(integerLiteral: lhs.rawValue + rhs.rawValue) + return Accidental(integerLiteral: lhs.rawValue + rhs.rawValue) } /// Returns a new accidental by substracting two accidentals in the equation. @@ -27,7 +27,7 @@ public func + (lhs: Accidental, rhs: Accidental) -> Accidental { /// - rhs: Right hand side of the equation. /// - Returns: Returns the difference of two accidentals. public func - (lhs: Accidental, rhs: Accidental) -> Accidental { - return Accidental(integerLiteral: lhs.rawValue - rhs.rawValue) + return Accidental(integerLiteral: lhs.rawValue - rhs.rawValue) } /// Returns a new accidental by adding up an int to the accidental in the equation. @@ -37,7 +37,7 @@ public func - (lhs: Accidental, rhs: Accidental) -> Accidental { /// - rhs: Right hand side of the equation. /// - Returns: Returns the sum of two accidentals. public func + (lhs: Accidental, rhs: Int) -> Accidental { - return Accidental(integerLiteral: lhs.rawValue + rhs) + return Accidental(integerLiteral: lhs.rawValue + rhs) } /// Returns a new accidental by substracting an int from the accidental in the equation. @@ -47,7 +47,7 @@ public func + (lhs: Accidental, rhs: Int) -> Accidental { /// - rhs: Right hand side of the equation. /// - Returns: Returns the difference of two accidentals. public func - (lhs: Accidental, rhs: Int) -> Accidental { - return Accidental(integerLiteral: lhs.rawValue - rhs) + return Accidental(integerLiteral: lhs.rawValue - rhs) } /// Multiples an accidental with a multiplier. @@ -57,7 +57,7 @@ public func - (lhs: Accidental, rhs: Int) -> Accidental { /// - rhs: Multiplier. /// - Returns: Returns a multiplied acceident. public func * (lhs: Accidental, rhs: Int) -> Accidental { - return Accidental(integerLiteral: lhs.rawValue * rhs) + return Accidental(integerLiteral: lhs.rawValue * rhs) } /// Divides an accidental with a multiplier @@ -67,7 +67,7 @@ public func * (lhs: Accidental, rhs: Int) -> Accidental { /// - rhs: Multiplier. /// - Returns: Returns a divided accidental. public func / (lhs: Accidental, rhs: Int) -> Accidental { - return Accidental(integerLiteral: lhs.rawValue / rhs) + return Accidental(integerLiteral: lhs.rawValue / rhs) } /// Checks if the two accidental is identical in terms of their halfstep values. @@ -77,7 +77,7 @@ public func / (lhs: Accidental, rhs: Int) -> Accidental { /// - rhs: Right hand side of the equation. /// - Returns: Returns true if two accidentalals is identical. public func == (lhs: Accidental, rhs: Accidental) -> Bool { - return lhs.rawValue == rhs.rawValue + return lhs.rawValue == rhs.rawValue } /// Checks if the two accidental is exactly identical. @@ -87,128 +87,128 @@ public func == (lhs: Accidental, rhs: Accidental) -> Bool { /// - rhs: Right hand side of the equation. /// - Returns: Returns true if two accidentalals is identical. public func === (lhs: Accidental, rhs: Accidental) -> Bool { - switch (lhs, rhs) { - case (.natural, .natural): - return true - case let (.sharps(a), .sharps(b)): - return a == b - case let (.flats(a), .flats(b)): - return a == b - default: - return false - } + switch (lhs, rhs) { + case (.natural, .natural): + return true + case let (.sharps(a), .sharps(b)): + return a == b + case let (.flats(a), .flats(b)): + return a == b + default: + return false + } } /// The enum used for calculating values of the `Key`s and `Pitche`s. public enum Accidental: Codable, Equatable, Hashable, RawRepresentable, ExpressibleByIntegerLiteral, ExpressibleByStringLiteral, CustomStringConvertible { - /// No accidental. - case natural - /// Reduces the `Key` or `Pitch` value amount of halfsteps. - case flats(amount: Int) - /// Increases the `Key` or `Pitch` value amount of halfsteps. - case sharps(amount: Int) - - /// Reduces the `Key` or `Pitch` value one halfstep below. - public static let flat: Accidental = .flats(amount: 1) - /// Increases the `Key` or `Pitch` value one halfstep above. - public static let sharp: Accidental = .sharps(amount: 1) - /// Reduces the `Key` or `Pitch` value amount two halfsteps below. - public static let doubleFlat: Accidental = .flats(amount: 2) - /// Increases the `Key` or `Pitch` value two halfsteps above. - public static let doubleSharp: Accidental = .sharps(amount: 2) - - /// A flag for `description` function that determines if it should use double sharp and double flat symbols. - /// It's useful to set it false where the fonts do not support that symbols. Defaults true. - public static var shouldUseDoubleFlatAndDoubleSharpNotation = true - - // MARK: RawRepresentable - - public typealias RawValue = Int - - /// Value of the accidental in terms of halfsteps. - public var rawValue: Int { - switch self { - case .natural: - return 0 - case let .flats(amount): - return -amount - case let .sharps(amount): - return amount + /// No accidental. + case natural + /// Reduces the `Key` or `Pitch` value amount of halfsteps. + case flats(amount: Int) + /// Increases the `Key` or `Pitch` value amount of halfsteps. + case sharps(amount: Int) + + /// Reduces the `Key` or `Pitch` value one halfstep below. + public static let flat: Accidental = .flats(amount: 1) + /// Increases the `Key` or `Pitch` value one halfstep above. + public static let sharp: Accidental = .sharps(amount: 1) + /// Reduces the `Key` or `Pitch` value amount two halfsteps below. + public static let doubleFlat: Accidental = .flats(amount: 2) + /// Increases the `Key` or `Pitch` value two halfsteps above. + public static let doubleSharp: Accidental = .sharps(amount: 2) + + /// A flag for `description` function that determines if it should use double sharp and double flat symbols. + /// It's useful to set it false where the fonts do not support that symbols. Defaults true. + public static var shouldUseDoubleFlatAndDoubleSharpNotation = true + + // MARK: RawRepresentable + + public typealias RawValue = Int + + /// Value of the accidental in terms of halfsteps. + public var rawValue: Int { + switch self { + case .natural: + return 0 + case let .flats(amount): + return -amount + case let .sharps(amount): + return amount + } } - } - - /// Initilizes the accidental with an integer that represents the halfstep amount. - /// - /// - Parameter rawValue: Halfstep value of the accidental. Zero if natural, above zero if sharp, below zero if flat. - public init?(rawValue: Accidental.RawValue) { - if rawValue == 0 { - self = .natural - } else if rawValue > 0 { - self = .sharps(amount: rawValue) - } else { - self = .flats(amount: -rawValue) + + /// Initilizes the accidental with an integer that represents the halfstep amount. + /// + /// - Parameter rawValue: Halfstep value of the accidental. Zero if natural, above zero if sharp, below zero if flat. + public init?(rawValue: Accidental.RawValue) { + if rawValue == 0 { + self = .natural + } else if rawValue > 0 { + self = .sharps(amount: rawValue) + } else { + self = .flats(amount: -rawValue) + } } - } - - // MARK: ExpressibleByIntegerLiteral - - public typealias IntegerLiteralType = Int - - /// Initilizes the accidental with an integer literal value. - /// - /// - Parameter value: Halfstep value of the accidental. Zero if natural, above zero if sharp, below zero if flat. - public init(integerLiteral value: Accidental.IntegerLiteralType) { - self = Accidental(rawValue: value) ?? .natural - } - - // MARK: ExpressibleByStringLiteral - - public typealias StringLiteralType = String - - public init(stringLiteral value: Accidental.StringLiteralType) { - var sum = 0 - for i in 0 ..< value.count { - switch value[value.index(value.startIndex, offsetBy: i)] { - case "#", "♯": - sum += 1 - case "b", "♭": - sum -= 1 - default: - break - } + + // MARK: ExpressibleByIntegerLiteral + + public typealias IntegerLiteralType = Int + + /// Initilizes the accidental with an integer literal value. + /// + /// - Parameter value: Halfstep value of the accidental. Zero if natural, above zero if sharp, below zero if flat. + public init(integerLiteral value: Accidental.IntegerLiteralType) { + self = Accidental(rawValue: value) ?? .natural } - self = Accidental(rawValue: sum) ?? .natural - } - - // MARK: CustomStringConvertible - - /// Returns the notation string of the accidental. - public var notation: String { - if case .natural = self { - return "♮" + + // MARK: ExpressibleByStringLiteral + + public typealias StringLiteralType = String + + public init(stringLiteral value: Accidental.StringLiteralType) { + var sum = 0 + for i in 0 ..< value.count { + switch value[value.index(value.startIndex, offsetBy: i)] { + case "#", "♯": + sum += 1 + case "b", "♭": + sum -= 1 + default: + break + } + } + self = Accidental(rawValue: sum) ?? .natural } - return description - } - - /// Returns the notation string of the accidental. Returns empty string if accidental is natural. - public var description: String { - switch self { - case .natural: - return "" - case let .flats(amount): - switch amount { - case 0: return Accidental.natural.description - case 1: return "♭" - case 2 where Accidental.shouldUseDoubleFlatAndDoubleSharpNotation: return "𝄫" - default: return amount > 0 ? (0 ..< amount).map({ _ in Accidental.flats(amount: 1).description }).joined() : "" - } - case let .sharps(amount): - switch amount { - case 0: return Accidental.natural.description - case 1: return "♯" - case 2 where Accidental.shouldUseDoubleFlatAndDoubleSharpNotation: return "𝄪" - default: return amount > 0 ? (0 ..< amount).map({ _ in Accidental.sharps(amount: 1).description }).joined() : "" - } + + // MARK: CustomStringConvertible + + /// Returns the notation string of the accidental. + public var notation: String { + if case .natural = self { + return "♮" + } + return description + } + + /// Returns the notation string of the accidental. Returns empty string if accidental is natural. + public var description: String { + switch self { + case .natural: + return "" + case let .flats(amount): + switch amount { + case 0: return Accidental.natural.description + case 1: return "♭" + case 2 where Accidental.shouldUseDoubleFlatAndDoubleSharpNotation: return "𝄫" + default: return amount > 0 ? (0 ..< amount).map({ _ in Accidental.flats(amount: 1).description }).joined() : "" + } + case let .sharps(amount): + switch amount { + case 0: return Accidental.natural.description + case 1: return "♯" + case 2 where Accidental.shouldUseDoubleFlatAndDoubleSharpNotation: return "𝄪" + default: return amount > 0 ? (0 ..< amount).map({ _ in Accidental.sharps(amount: 1).description }).joined() : "" + } + } } - } } diff --git a/Sources/MusicTheory/Chord.swift b/Sources/MusicTheory/Chord.swift index 65c8553..c19487a 100644 --- a/Sources/MusicTheory/Chord.swift +++ b/Sources/MusicTheory/Chord.swift @@ -14,422 +14,422 @@ import Foundation /// Protocol that defines a printable chord part. public protocol ChordDescription: CustomStringConvertible, Codable { - /// Notation of chord. - var notation: String { get } + /// Notation of chord. + var notation: String { get } } /// Protocol that defines a chord part. public protocol ChordPart: ChordDescription { - /// Interval between the root. - var interval: Interval { get } - /// Initilize chord part with interval. - init?(interval: Interval) + /// Interval between the root. + var interval: Interval { get } + /// Initilize chord part with interval. + init?(interval: Interval) } extension Interval { - /// Returns the sum of two intervals semitones. - /// - /// - Parameters: - /// - lhs: Left hand side of the equation. - /// - rhs: Right hand side of the equation. - /// - Returns: Sum of two intervals in terms of their semitones. - fileprivate static func + (lhs: Interval, rhs: Accidental) -> Int { - return lhs.semitones + rhs.rawValue - } + /// Returns the sum of two intervals semitones. + /// + /// - Parameters: + /// - lhs: Left hand side of the equation. + /// - rhs: Right hand side of the equation. + /// - Returns: Sum of two intervals in terms of their semitones. + fileprivate static func + (lhs: Interval, rhs: Accidental) -> Int { + return lhs.semitones + rhs.rawValue + } } /// Defines third part of the chord. Second note after the root. public enum ChordThirdType: Int, ChordPart { - /// Defines major chord. 4 halfsteps between root. - case major - /// Defines minor chord. 3 halfsteps between root. - case minor - - /// Initilize chord part with interval. - public init?(interval: Interval) { - switch interval { - case ChordThirdType.major.interval: - self = .major - case ChordThirdType.minor.interval: - self = .minor - default: - return nil + /// Defines major chord. 4 halfsteps between root. + case major + /// Defines minor chord. 3 halfsteps between root. + case minor + + /// Initilize chord part with interval. + public init?(interval: Interval) { + switch interval { + case ChordThirdType.major.interval: + self = .major + case ChordThirdType.minor.interval: + self = .minor + default: + return nil + } } - } - - /// Interval between root. - public var interval: Interval { - switch self { - case .major: - return .M3 - case .minor: - return .m3 - } - } - - /// Notation of chord part. - public var notation: String { - switch self { - case .major: return "" - case .minor: return "m" + + /// Interval between root. + public var interval: Interval { + switch self { + case .major: + return .M3 + case .minor: + return .m3 + } } - } - - /// Description of chord part. - public var description: String { - switch self { - case .major: return "Major" - case .minor: return "Minor" + + /// Notation of chord part. + public var notation: String { + switch self { + case .major: return "" + case .minor: return "m" + } + } + + /// Description of chord part. + public var description: String { + switch self { + case .major: return "Major" + case .minor: return "Minor" + } + } + + /// All values of `ChordThirdType`. + public static var all: [ChordThirdType] { + return [.major, .minor] } - } - - /// All values of `ChordThirdType`. - public static var all: [ChordThirdType] { - return [.major, .minor] - } } /// Defines fifth part of the chord. Third note after root note. public enum ChordFifthType: Int, ChordPart { - /// Perfect fifth interval between root. - case perfect - /// Half step down of perfect fifth. - case diminished - /// Half step up of perfect fifth. - case augmented - - /// Initilize chord part with interval. - public init?(interval: Interval) { - switch interval { - case ChordFifthType.perfect.interval: - self = .perfect - case ChordFifthType.diminished.interval: - self = .diminished - case ChordFifthType.augmented.interval: - self = .augmented - default: - return nil + /// Perfect fifth interval between root. + case perfect + /// Half step down of perfect fifth. + case diminished + /// Half step up of perfect fifth. + case augmented + + /// Initilize chord part with interval. + public init?(interval: Interval) { + switch interval { + case ChordFifthType.perfect.interval: + self = .perfect + case ChordFifthType.diminished.interval: + self = .diminished + case ChordFifthType.augmented.interval: + self = .augmented + default: + return nil + } + } + + /// Interval between root. + public var interval: Interval { + switch self { + case .perfect: + return .P5 + case .diminished: + return .d5 + case .augmented: + return .A5 + } + } + + /// Notation of chord part. + public var notation: String { + switch self { + case .perfect: return "" + case .augmented: return "♯5" + case .diminished: return "♭5" + } + } + + /// Description of chord part. + public var description: String { + switch self { + case .perfect: return "" + case .augmented: return "Augmented" + case .diminished: return "Diminished" + } + } + + /// All values of `ChordFifthType`. + public static var all: [ChordFifthType] { + return [.perfect, .diminished, .augmented] } - } - - /// Interval between root. - public var interval: Interval { - switch self { - case .perfect: - return .P5 - case .diminished: - return .d5 - case .augmented: - return .A5 - } - } - - /// Notation of chord part. - public var notation: String { - switch self { - case .perfect: return "" - case .augmented: return "♯5" - case .diminished: return "♭5" - } - } - - /// Description of chord part. - public var description: String { - switch self { - case .perfect: return "" - case .augmented: return "Augmented" - case .diminished: return "Diminished" - } - } - - /// All values of `ChordFifthType`. - public static var all: [ChordFifthType] { - return [.perfect, .diminished, .augmented] - } } /// Defiens sixth chords. If you add the sixth note, you have sixth chord. public struct ChordSixthType: ChordPart { - /// Default initilizer. - public init() {} - - /// Initilize chord part with interval. - public init?(interval: Interval) { - switch interval { - case .M6: - self.init() - default: - return nil + /// Default initilizer. + public init() {} + + /// Initilize chord part with interval. + public init?(interval: Interval) { + switch interval { + case .M6: + self.init() + default: + return nil + } + } + + /// Interval between root. + public var interval: Interval { + return .M6 + } + + /// Notation of chord part. + public var notation: String { + return "6" + } + + /// Description of chord part. + public var description: String { + return "Sixth" } - } - - /// Interval between root. - public var interval: Interval { - return .M6 - } - - /// Notation of chord part. - public var notation: String { - return "6" - } - - /// Description of chord part. - public var description: String { - return "Sixth" - } } /// Defiens seventh chords. If you add seventh note, you have seventh chord. public enum ChordSeventhType: Int, ChordPart { - /// Seventh note of the chord. 11 halfsteps between the root. - case major - /// Halfstep down of a seventh note. 10 halfsteps between the root. - case dominant - /// Two halfsteps down of a seventh note. 9 halfsteps between the root. - case diminished - - /// Initilize chord part with interval. - public init?(interval: Interval) { - switch interval { - case ChordSeventhType.major.interval: - self = .major - case ChordSeventhType.dominant.interval: - self = .dominant - case ChordSeventhType.diminished.interval: - self = .diminished - default: - return nil + /// Seventh note of the chord. 11 halfsteps between the root. + case major + /// Halfstep down of a seventh note. 10 halfsteps between the root. + case dominant + /// Two halfsteps down of a seventh note. 9 halfsteps between the root. + case diminished + + /// Initilize chord part with interval. + public init?(interval: Interval) { + switch interval { + case ChordSeventhType.major.interval: + self = .major + case ChordSeventhType.dominant.interval: + self = .dominant + case ChordSeventhType.diminished.interval: + self = .diminished + default: + return nil + } + } + + /// Interval between root. + public var interval: Interval { + switch self { + case .major: + return .M7 + case .dominant: + return .m7 + case .diminished: + return Interval(quality: .diminished, degree: 7, semitones: 9) + } + } + + /// Notation of chord part. + public var notation: String { + switch self { + case .major: return "M7" + case .dominant: return "7" + case .diminished: return "°7" + } + } + + /// Description of chord part. + public var description: String { + switch self { + case .major: return "Major 7th" + case .dominant: return "Dominant 7th" + case .diminished: return "Diminished 7th" + } + } + + /// All values of `ChordSeventhType`. + public static var all: [ChordSeventhType] { + return [.major, .dominant, .diminished] } - } - - /// Interval between root. - public var interval: Interval { - switch self { - case .major: - return .M7 - case .dominant: - return .m7 - case .diminished: - return Interval(quality: .diminished, degree: 7, semitones: 9) - } - } - - /// Notation of chord part. - public var notation: String { - switch self { - case .major: return "M7" - case .dominant: return "7" - case .diminished: return "°7" - } - } - - /// Description of chord part. - public var description: String { - switch self { - case .major: return "Major 7th" - case .dominant: return "Dominant 7th" - case .diminished: return "Diminished 7th" - } - } - - /// All values of `ChordSeventhType`. - public static var all: [ChordSeventhType] { - return [.major, .dominant, .diminished] - } } /// Defines suspended chords. /// If you play second or fourth note of chord, instead of thirds, than you have suspended chords. public enum ChordSuspendedType: Int, ChordPart { - /// Second note of chord instead of third part. 2 halfsteps between root. - case sus2 - /// Fourth note of chord instead of third part. 5 halfsteps between root. - case sus4 - - /// Initilize chord part with interval - public init?(interval: Interval) { - switch interval { - case ChordSuspendedType.sus2.interval: - self = .sus2 - case ChordSuspendedType.sus4.interval: - self = .sus4 - default: - return nil + /// Second note of chord instead of third part. 2 halfsteps between root. + case sus2 + /// Fourth note of chord instead of third part. 5 halfsteps between root. + case sus4 + + /// Initilize chord part with interval + public init?(interval: Interval) { + switch interval { + case ChordSuspendedType.sus2.interval: + self = .sus2 + case ChordSuspendedType.sus4.interval: + self = .sus4 + default: + return nil + } } - } - - /// Interval between root. - public var interval: Interval { - switch self { - case .sus2: - return .M2 - case .sus4: - return .P4 - } - } - - /// Notation of chord part. - public var notation: String { - switch self { - case .sus2: return "(sus2)" - case .sus4: return "(sus4)" + + /// Interval between root. + public var interval: Interval { + switch self { + case .sus2: + return .M2 + case .sus4: + return .P4 + } } - } - - /// Description of chord part. - public var description: String { - switch self { - case .sus2: return "Suspended 2nd" - case .sus4: return "Suspended 4th" + + /// Notation of chord part. + public var notation: String { + switch self { + case .sus2: return "(sus2)" + case .sus4: return "(sus4)" + } + } + + /// Description of chord part. + public var description: String { + switch self { + case .sus2: return "Suspended 2nd" + case .sus4: return "Suspended 4th" + } + } + + /// All values of `ChordSuspendedType`. + public static var all: [ChordSuspendedType] { + return [.sus2, .sus4] } - } - - /// All values of `ChordSuspendedType`. - public static var all: [ChordSuspendedType] { - return [.sus2, .sus4] - } } /// Defines extended chords. /// If you add one octave up of second, fourth or sixth notes of the chord, you have extended chords. /// You can combine extended chords more than one in a chord. public struct ChordExtensionType: ChordPart { - /// Defines type of the extended chords. - public enum ExtensionType: Int, ChordDescription { - /// 9th chord. Second note of the chord, one octave up from root. - case ninth = 9 - /// 11th chord. Eleventh note of the chord, one octave up from root. - case eleventh = 11 - /// 13th chord. Sixth note of the chord, one octave up from root. - case thirteenth = 13 - + /// Defines type of the extended chords. + public enum ExtensionType: Int, ChordDescription { + /// 9th chord. Second note of the chord, one octave up from root. + case ninth = 9 + /// 11th chord. Eleventh note of the chord, one octave up from root. + case eleventh = 11 + /// 13th chord. Sixth note of the chord, one octave up from root. + case thirteenth = 13 + + /// Interval between root. + public var interval: Interval { + switch self { + case .ninth: + return .M9 + case .eleventh: + return .P11 + case .thirteenth: + return .M13 + } + } + + /// Notation of the chord part. + public var notation: String { + switch self { + case .ninth: + return "9" + case .eleventh: + return "11" + case .thirteenth: + return "13" + } + } + + /// Description of the chord part. + public var description: String { + switch self { + case .ninth: + return "9th" + case .eleventh: + return "11th" + case .thirteenth: + return "13th" + } + } + + /// All values of `ExtensionType`. + public static var all: [ExtensionType] { + return [.ninth, .eleventh, .thirteenth] + } + } + + /// Type of extended chord. + public var type: ExtensionType + /// Accident of extended chord. + public var accidental: Accidental + /// If there are no seventh note and only one extended part is this. Defaults false + internal var isAdded: Bool + + /// Initilizes extended chord. + /// + /// - Parameters: + /// - type: Type of extended chord. + /// - accident: Accident of extended chord. Defaults natural. + public init(type: ExtensionType, accidental: Accidental = .natural) { + self.type = type + self.accidental = accidental + isAdded = false + } + + /// Initilize chord part with interval + public init?(interval: Interval) { + switch interval.semitones { + case ExtensionType.ninth.interval + Accidental.natural: + self = ChordExtensionType(type: .ninth, accidental: .natural) + case ExtensionType.ninth.interval + Accidental.flat: + self = ChordExtensionType(type: .ninth, accidental: .flat) + case ExtensionType.ninth.interval + Accidental.sharp: + self = ChordExtensionType(type: .ninth, accidental: .sharp) + case ExtensionType.eleventh.interval + Accidental.natural: + self = ChordExtensionType(type: .eleventh, accidental: .natural) + case ExtensionType.eleventh.interval + Accidental.flat: + self = ChordExtensionType(type: .eleventh, accidental: .flat) + case ExtensionType.eleventh.interval + Accidental.sharp: + self = ChordExtensionType(type: .eleventh, accidental: .sharp) + case ExtensionType.thirteenth.interval + Accidental.natural: + self = ChordExtensionType(type: .thirteenth, accidental: .natural) + case ExtensionType.thirteenth.interval + Accidental.flat: + self = ChordExtensionType(type: .thirteenth, accidental: .flat) + case ExtensionType.thirteenth.interval + Accidental.sharp: + self = ChordExtensionType(type: .thirteenth, accidental: .sharp) + default: + return nil + } + } + /// Interval between root. public var interval: Interval { - switch self { - case .ninth: - return .M9 - case .eleventh: - return .P11 - case .thirteenth: - return .M13 - } + switch (type, accidental) { + case (.ninth, .natural): return .M9 + case (.ninth, .flat): return .m9 + case (.ninth, .sharp): return .A9 + + case (.eleventh, .natural): return .P11 + case (.eleventh, .flat): return .P11 + case (.eleventh, .sharp): return .A11 + + case (.thirteenth, .natural): return .M13 + case (.thirteenth, .flat): return .m13 + case (.thirteenth, .sharp): return .A13 + + case (.ninth, _): return .M9 + case (.eleventh, _): return .P11 + case (.thirteenth, _): return .M13 + } } - - /// Notation of the chord part. + + /// Notation of chord part. public var notation: String { - switch self { - case .ninth: - return "9" - case .eleventh: - return "11" - case .thirteenth: - return "13" - } + return "\(isAdded ? "add" : "")\(accidental.notation)\(type.notation)" } - - /// Description of the chord part. + + /// Description of chord part. public var description: String { - switch self { - case .ninth: - return "9th" - case .eleventh: - return "11th" - case .thirteenth: - return "13th" - } - } - - /// All values of `ExtensionType`. - public static var all: [ExtensionType] { - return [.ninth, .eleventh, .thirteenth] - } - } - - /// Type of extended chord. - public var type: ExtensionType - /// Accident of extended chord. - public var accidental: Accidental - /// If there are no seventh note and only one extended part is this. Defaults false - internal var isAdded: Bool - - /// Initilizes extended chord. - /// - /// - Parameters: - /// - type: Type of extended chord. - /// - accident: Accident of extended chord. Defaults natural. - public init(type: ExtensionType, accidental: Accidental = .natural) { - self.type = type - self.accidental = accidental - isAdded = false - } - - /// Initilize chord part with interval - public init?(interval: Interval) { - switch interval.semitones { - case ExtensionType.ninth.interval + Accidental.natural: - self = ChordExtensionType(type: .ninth, accidental: .natural) - case ExtensionType.ninth.interval + Accidental.flat: - self = ChordExtensionType(type: .ninth, accidental: .flat) - case ExtensionType.ninth.interval + Accidental.sharp: - self = ChordExtensionType(type: .ninth, accidental: .sharp) - case ExtensionType.eleventh.interval + Accidental.natural: - self = ChordExtensionType(type: .eleventh, accidental: .natural) - case ExtensionType.eleventh.interval + Accidental.flat: - self = ChordExtensionType(type: .eleventh, accidental: .flat) - case ExtensionType.eleventh.interval + Accidental.sharp: - self = ChordExtensionType(type: .eleventh, accidental: .sharp) - case ExtensionType.thirteenth.interval + Accidental.natural: - self = ChordExtensionType(type: .thirteenth, accidental: .natural) - case ExtensionType.thirteenth.interval + Accidental.flat: - self = ChordExtensionType(type: .thirteenth, accidental: .flat) - case ExtensionType.thirteenth.interval + Accidental.sharp: - self = ChordExtensionType(type: .thirteenth, accidental: .sharp) - default: - return nil - } - } - - /// Interval between root. - public var interval: Interval { - switch (type, accidental) { - case (.ninth, .natural): return .M9 - case (.ninth, .flat): return .m9 - case (.ninth, .sharp): return .A9 - - case (.eleventh, .natural): return .P11 - case (.eleventh, .flat): return .P11 - case (.eleventh, .sharp): return .A11 - - case (.thirteenth, .natural): return .M13 - case (.thirteenth, .flat): return .m13 - case (.thirteenth, .sharp): return .A13 - - case (.ninth, _): return .M9 - case (.eleventh, _): return .P11 - case (.thirteenth, _): return .M13 + return "\(isAdded ? "Added " : "")\(accidental.description) \(type.description)" + } + + /// All values of `ChordExtensionType` + public static var all: [ChordExtensionType] { + var all = [ChordExtensionType]() + for type in ExtensionType.all { + for accident in [Accidental.natural, Accidental.flat, Accidental.sharp] { + all.append(ChordExtensionType(type: type, accidental: accident)) + } + } + return all } - } - - /// Notation of chord part. - public var notation: String { - return "\(isAdded ? "add" : "")\(accidental.notation)\(type.notation)" - } - - /// Description of chord part. - public var description: String { - return "\(isAdded ? "Added " : "")\(accidental.description) \(type.description)" - } - - /// All values of `ChordExtensionType` - public static var all: [ChordExtensionType] { - var all = [ChordExtensionType]() - for type in ExtensionType.all { - for accident in [Accidental.natural, Accidental.flat, Accidental.sharp] { - all.append(ChordExtensionType(type: type, accidental: accident)) - } - } - return all - } } // MARK: - ChordType @@ -441,242 +441,242 @@ public struct ChordExtensionType: ChordPart { /// - right: Right handside of the equation. /// - Returns: Returns Bool value of equation of two given chord types. public func == (left: ChordType?, right: ChordType?) -> Bool { - switch (left, right) { - case let (.some(left), .some(right)): - return left.intervals == right.intervals - case (.none, .none): - return true - default: - return false - } + switch (left, right) { + case let (.some(left), .some(right)): + return left.intervals == right.intervals + case (.none, .none): + return true + default: + return false + } } /// Defines full type of chord with all chord parts. public struct ChordType: ChordDescription, Hashable { - /// Thirds part. Second note of the chord. - public var third: ChordThirdType - /// Fifths part. Third note of the chord. - public var fifth: ChordFifthType - /// Defines a sixth chord. Defaults nil. - public var sixth: ChordSixthType? - /// Defines a seventh chord. Defaults nil. - public var seventh: ChordSeventhType? - /// Defines a suspended chord. Defaults nil. - public var suspended: ChordSuspendedType? - /// Defines extended chord. Defaults nil. - public var extensions: [ChordExtensionType]? { - didSet { - if extensions?.count == 1 { - extensions![0].isAdded = seventh == nil - // Add other extensions if needed - if let ext = extensions?.first, ext.type == .eleventh, !ext.isAdded { - extensions?.append(ChordExtensionType(type: .ninth)) - } else if let ext = extensions?.first, ext.type == .thirteenth, !ext.isAdded { - extensions?.append(ChordExtensionType(type: .ninth)) - extensions?.append(ChordExtensionType(type: .eleventh)) - } - } - } - } - - /// Initilze the chord type with its parts. - /// - /// - Parameters: - /// - third: Thirds part. - /// - fifth: Fifths part. Defaults perfect fifth. - /// - sixth: Sixth part. Defaults nil. - /// - seventh: Seventh part. Defaults nil. - /// - suspended: Suspended part. Defaults nil. - /// - extensions: Extended chords part. Defaults nil. Could be add more than one extended chord. - /// - custom: Fill that with the custom intervals if it's not represented by the current data structures. Defaults nil. - public init( - third: ChordThirdType, - fifth: ChordFifthType = .perfect, - sixth: ChordSixthType? = nil, - seventh: ChordSeventhType? = nil, - suspended: ChordSuspendedType? = nil, - extensions: [ChordExtensionType]? = nil) { - self.third = third - self.fifth = fifth - self.sixth = sixth - self.seventh = seventh - self.suspended = suspended - self.extensions = extensions - - if extensions?.count == 1 { - self.extensions![0].isAdded = seventh == nil - // Add other extensions if needed - if let ext = self.extensions?.first, ext.type == .eleventh, !ext.isAdded { - self.extensions?.append(ChordExtensionType(type: .ninth)) - } else if let ext = self.extensions?.first, ext.type == .thirteenth, !ext.isAdded { - self.extensions?.append(ChordExtensionType(type: .ninth)) - self.extensions?.append(ChordExtensionType(type: .eleventh)) - } - } - } - - /// Initilze the chord type with its intervals. - /// - /// - Parameters: - /// - intervals: Intervals of chord notes distances between root note for each. - public init?(intervals: [Interval]) { - var third: ChordThirdType? - var fifth: ChordFifthType? - var sixth: ChordSixthType? - var seventh: ChordSeventhType? - var suspended: ChordSuspendedType? - var extensions = [ChordExtensionType]() - - for interval in intervals { - if let thirdPart = ChordThirdType(interval: interval) { - third = thirdPart - } else if let fifthPart = ChordFifthType(interval: interval) { - fifth = fifthPart - } else if let sixthPart = ChordSixthType(interval: interval) { - sixth = sixthPart - } else if let seventhPart = ChordSeventhType(interval: interval) { - seventh = seventhPart - } else if let suspendedPart = ChordSuspendedType(interval: interval) { - suspended = suspendedPart - } else if let extensionPart = ChordExtensionType(interval: interval) { - extensions.append(extensionPart) - } - } - - self = ChordType( - third: third ?? .major, - fifth: fifth ?? .perfect, - sixth: sixth, - seventh: seventh, - suspended: suspended, - extensions: extensions) - } - - /// Intervals of parts between root. - public var intervals: [Interval] { - var parts: [ChordPart?] = [third, suspended, fifth, sixth, seventh] - parts += extensions?.sorted(by: { $0.type.rawValue < $1.type.rawValue }).map({ $0 as ChordPart? }) ?? [] - return [.P1] + parts.compactMap({ $0?.interval }) - } - - /// Notation of the chord type. - public var notation: String { - var seventhNotation = seventh?.notation ?? "" - var sixthNotation = sixth == nil ? "" : "\(sixth!.notation)\(seventh == nil ? "" : "/")" - let suspendedNotation = suspended?.notation ?? "" - var extensionNotation = "" - let ext = extensions?.sorted(by: { $0.type.rawValue < $1.type.rawValue }) ?? [] - - var singleNotation = !ext.isEmpty && true - for i in 0 ..< max(0, ext.count - 1) { - if ext[i].accidental != .natural { - singleNotation = false - } - } - - if singleNotation { - extensionNotation = "(\(ext.last!.notation))" - } else { - extensionNotation = ext - .compactMap({ $0.notation }) - .joined(separator: "/") - extensionNotation = extensionNotation.isEmpty ? "" : "(\(extensionNotation))" - } - - if seventh != nil { - // Don't show major seventh note if extended is a major as well - if (extensions ?? []).count > 0 { - switch seventh! { - case .diminished: seventhNotation = "°" - case .major: seventhNotation = "M" - case .dominant: seventhNotation = "" - } - sixthNotation = sixth == nil ? "" : sixth!.notation - } - // Show fifth note after seventh in parenthesis - if fifth == .augmented || fifth == .diminished { - return "\(third.notation)\(sixthNotation)\(seventhNotation)(\(fifth.notation))\(suspendedNotation)\(extensionNotation)" - } + /// Thirds part. Second note of the chord. + public var third: ChordThirdType + /// Fifths part. Third note of the chord. + public var fifth: ChordFifthType + /// Defines a sixth chord. Defaults nil. + public var sixth: ChordSixthType? + /// Defines a seventh chord. Defaults nil. + public var seventh: ChordSeventhType? + /// Defines a suspended chord. Defaults nil. + public var suspended: ChordSuspendedType? + /// Defines extended chord. Defaults nil. + public var extensions: [ChordExtensionType]? { + didSet { + if extensions?.count == 1 { + extensions![0].isAdded = seventh == nil + // Add other extensions if needed + if let ext = extensions?.first, ext.type == .eleventh, !ext.isAdded { + extensions?.append(ChordExtensionType(type: .ninth)) + } else if let ext = extensions?.first, ext.type == .thirteenth, !ext.isAdded { + extensions?.append(ChordExtensionType(type: .ninth)) + extensions?.append(ChordExtensionType(type: .eleventh)) + } + } + } } - - return "\(third.notation)\(fifth.notation)\(sixthNotation)\(seventhNotation)\(suspendedNotation)\(extensionNotation)" - } - - /// Description of the chord type. - public var description: String { - let seventhNotation = seventh?.description - let sixthNotation = sixth?.description - let suspendedNotation = suspended?.description - let extensionsNotation = extensions? - .sorted(by: { $0.type.rawValue < $1.type.rawValue }) - .map({ $0.description as String? }) ?? [] - let desc = [third.description, fifth.description, sixthNotation, seventhNotation, suspendedNotation] + extensionsNotation - return desc.compactMap({ $0 }).joined(separator: " ") - } - - /// All possible chords could be generated. - public static var all: [ChordType] { - func combinations(_ elements: [ChordExtensionType], taking: Int = 1) -> [[ChordExtensionType]] { - guard elements.count >= taking else { return [] } - guard elements.count > 0 && taking > 0 else { return [[]] } - - if taking == 1 { - return elements.map { [$0] } - } - - var comb = [[ChordExtensionType]]() - for (index, element) in elements.enumerated() { - var reducedElements = elements - reducedElements.removeFirst(index + 1) - comb += combinations(reducedElements, taking: taking - 1).map { [element] + $0 } - } - return comb + + /// Initilze the chord type with its parts. + /// + /// - Parameters: + /// - third: Thirds part. + /// - fifth: Fifths part. Defaults perfect fifth. + /// - sixth: Sixth part. Defaults nil. + /// - seventh: Seventh part. Defaults nil. + /// - suspended: Suspended part. Defaults nil. + /// - extensions: Extended chords part. Defaults nil. Could be add more than one extended chord. + /// - custom: Fill that with the custom intervals if it's not represented by the current data structures. Defaults nil. + public init( + third: ChordThirdType, + fifth: ChordFifthType = .perfect, + sixth: ChordSixthType? = nil, + seventh: ChordSeventhType? = nil, + suspended: ChordSuspendedType? = nil, + extensions: [ChordExtensionType]? = nil) { + self.third = third + self.fifth = fifth + self.sixth = sixth + self.seventh = seventh + self.suspended = suspended + self.extensions = extensions + + if extensions?.count == 1 { + self.extensions![0].isAdded = seventh == nil + // Add other extensions if needed + if let ext = self.extensions?.first, ext.type == .eleventh, !ext.isAdded { + self.extensions?.append(ChordExtensionType(type: .ninth)) + } else if let ext = self.extensions?.first, ext.type == .thirteenth, !ext.isAdded { + self.extensions?.append(ChordExtensionType(type: .ninth)) + self.extensions?.append(ChordExtensionType(type: .eleventh)) + } + } + } + + /// Initilze the chord type with its intervals. + /// + /// - Parameters: + /// - intervals: Intervals of chord notes distances between root note for each. + public init?(intervals: [Interval]) { + var third: ChordThirdType? + var fifth: ChordFifthType? + var sixth: ChordSixthType? + var seventh: ChordSeventhType? + var suspended: ChordSuspendedType? + var extensions = [ChordExtensionType]() + + for interval in intervals { + if let thirdPart = ChordThirdType(interval: interval) { + third = thirdPart + } else if let fifthPart = ChordFifthType(interval: interval) { + fifth = fifthPart + } else if let sixthPart = ChordSixthType(interval: interval) { + sixth = sixthPart + } else if let seventhPart = ChordSeventhType(interval: interval) { + seventh = seventhPart + } else if let suspendedPart = ChordSuspendedType(interval: interval) { + suspended = suspendedPart + } else if let extensionPart = ChordExtensionType(interval: interval) { + extensions.append(extensionPart) + } + } + + self = ChordType( + third: third ?? .major, + fifth: fifth ?? .perfect, + sixth: sixth, + seventh: seventh, + suspended: suspended, + extensions: extensions) + } + + /// Intervals of parts between root. + public var intervals: [Interval] { + var parts: [ChordPart?] = [third, suspended, fifth, sixth, seventh] + parts += extensions?.sorted(by: { $0.type.rawValue < $1.type.rawValue }).map({ $0 as ChordPart? }) ?? [] + return [.P1] + parts.compactMap({ $0?.interval }) + } + + /// Notation of the chord type. + public var notation: String { + var seventhNotation = seventh?.notation ?? "" + var sixthNotation = sixth == nil ? "" : "\(sixth!.notation)\(seventh == nil ? "" : "/")" + let suspendedNotation = suspended?.notation ?? "" + var extensionNotation = "" + let ext = extensions?.sorted(by: { $0.type.rawValue < $1.type.rawValue }) ?? [] + + var singleNotation = !ext.isEmpty && true + for i in 0 ..< max(0, ext.count - 1) { + if ext[i].accidental != .natural { + singleNotation = false + } + } + + if singleNotation { + extensionNotation = "(\(ext.last!.notation))" + } else { + extensionNotation = ext + .compactMap({ $0.notation }) + .joined(separator: "/") + extensionNotation = extensionNotation.isEmpty ? "" : "(\(extensionNotation))" + } + + if seventh != nil { + // Don't show major seventh note if extended is a major as well + if (extensions ?? []).count > 0 { + switch seventh! { + case .diminished: seventhNotation = "°" + case .major: seventhNotation = "M" + case .dominant: seventhNotation = "" + } + sixthNotation = sixth == nil ? "" : sixth!.notation + } + // Show fifth note after seventh in parenthesis + if fifth == .augmented || fifth == .diminished { + return "\(third.notation)\(sixthNotation)\(seventhNotation)(\(fifth.notation))\(suspendedNotation)\(extensionNotation)" + } + } + + return "\(third.notation)\(fifth.notation)\(sixthNotation)\(seventhNotation)\(suspendedNotation)\(extensionNotation)" } - - var all = [ChordType]() - let allThird = ChordThirdType.all - let allFifth = ChordFifthType.all - let allSixth: [ChordSixthType?] = [ChordSixthType(), nil] - let allSeventh: [ChordSeventhType?] = ChordSeventhType.all + [nil] - let allSus: [ChordSuspendedType?] = ChordSuspendedType.all + [nil] - let allExt = combinations(ChordExtensionType.all) + - combinations(ChordExtensionType.all, taking: 2) + - combinations(ChordExtensionType.all, taking: 3) - - for third in allThird { - for fifth in allFifth { - for sixth in allSixth { - for seventh in allSeventh { - for sus in allSus { - for ext in allExt { - all.append(ChordType( - third: third, - fifth: fifth, - sixth: sixth, - seventh: seventh, - suspended: sus, - extensions: ext - )) - } + + /// Description of the chord type. + public var description: String { + let seventhNotation = seventh?.description + let sixthNotation = sixth?.description + let suspendedNotation = suspended?.description + let extensionsNotation = extensions? + .sorted(by: { $0.type.rawValue < $1.type.rawValue }) + .map({ $0.description as String? }) ?? [] + let desc = [third.description, fifth.description, sixthNotation, seventhNotation, suspendedNotation] + extensionsNotation + return desc.compactMap({ $0 }).joined(separator: " ") + } + + /// All possible chords could be generated. + public static var all: [ChordType] { + func combinations(_ elements: [ChordExtensionType], taking: Int = 1) -> [[ChordExtensionType]] { + guard elements.count >= taking else { return [] } + guard elements.count > 0 && taking > 0 else { return [[]] } + + if taking == 1 { + return elements.map { [$0] } + } + + var comb = [[ChordExtensionType]]() + for (index, element) in elements.enumerated() { + var reducedElements = elements + reducedElements.removeFirst(index + 1) + comb += combinations(reducedElements, taking: taking - 1).map { [element] + $0 } } - } + return comb } - } + + var all = [ChordType]() + let allThird = ChordThirdType.all + let allFifth = ChordFifthType.all + let allSixth: [ChordSixthType?] = [ChordSixthType(), nil] + let allSeventh: [ChordSeventhType?] = ChordSeventhType.all + [nil] + let allSus: [ChordSuspendedType?] = ChordSuspendedType.all + [nil] + let allExt = combinations(ChordExtensionType.all) + + combinations(ChordExtensionType.all, taking: 2) + + combinations(ChordExtensionType.all, taking: 3) + + for third in allThird { + for fifth in allFifth { + for sixth in allSixth { + for seventh in allSeventh { + for sus in allSus { + for ext in allExt { + all.append(ChordType( + third: third, + fifth: fifth, + sixth: sixth, + seventh: seventh, + suspended: sus, + extensions: ext + )) + } + } + } + } + } + } + return all + } + + // MARK: Hashable + + public func hash(into hasher: inout Hasher) { + hasher.combine(intervals) + } + + // MARK: Equatable + + public static func == (lhs: ChordType, rhs: ChordType) -> Bool { + return lhs.hashValue == rhs.hashValue } - return all - } - - // MARK: Hashable - - public func hash(into hasher: inout Hasher) { - hasher.combine(intervals) - } - - // MARK: Equatable - - public static func == (lhs: ChordType, rhs: ChordType) -> Bool { - return lhs.hashValue == rhs.hashValue - } } // MARK: - Chord @@ -688,149 +688,149 @@ public struct ChordType: ChordDescription, Hashable { /// - right: Right handside of the equation. /// - Returns: Returns Bool value of equation of two given chords. public func == (left: Chord?, right: Chord?) -> Bool { - switch (left, right) { - case let (.some(left), .some(right)): - return left.key == right.key && left.type == right.type && left.inversion == right.inversion - case (.none, .none): - return true - default: - return false - } + switch (left, right) { + case let (.some(left), .some(right)): + return left.key == right.key && left.type == right.type && left.inversion == right.inversion + case (.none, .none): + return true + default: + return false + } } /// Defines a chord with a root note and type. public struct Chord: ChordDescription { - /// Type of the chord. - public var type: ChordType - /// Root key of the chord. - public var key: Key - /// Inversion index of the chord. - public private(set) var inversion: Int - - /// Initilizes chord with root note and type. - /// - /// - Parameters: - /// - key: Root key of the chord. - /// - type: Tyoe of the chord. - public init(type: ChordType, key: Key, inversion: Int = 0) { - self.type = type - self.key = key - self.inversion = inversion - } - - /// Types of notes in chord. - public var keys: [Key] { - return pitches(octave: 1).map({ $0.key }) - } - - /// Possible inversions of the chord. - public var inversions: [Chord] { - return [Int](0 ..< keys.count).map({ Chord(type: type, key: key, inversion: $0) }) - } - - /// Notation of the chord. - public var notation: String { - let inversionNotation = inversion > 0 && inversion < keys.count ? "/\(keys[0])" : "" - return "\(key)\(type.notation)\(inversionNotation)" - } - - /// Generates notes of the chord for octave. - /// - /// - Parameter octave: Octave of the root note for the build chord from. - /// - Returns: Generates notes of the chord. - public func pitches(octave: Int) -> [Pitch] { - var intervals = type.intervals - for _ in 0 ..< inversion { - intervals = intervals.shifted - } - - let root = Pitch(key: key, octave: octave) - let invertedPitches = intervals.map({ root + $0 }) - return invertedPitches - .enumerated() - .map({ index, item in - index < type.intervals.count - inversion ? item : Pitch(key: item.key, octave: item.octave + 1) - }) - } - - /// Generates notes of the chord for octave range. - /// - /// - Parameter octaves: Octaves of the root note to build chord from. - /// - Returns: Generates notes of the chord. - public func pitches(octaves: [Int]) -> [Pitch] { - return octaves.flatMap({ pitches(octave: $0) }).sorted(by: { $0.rawValue < $1.rawValue }) - } - - /// Returns the roman numeric string for a chord. - /// - /// - Parameter scale: The scale that the chord in. - /// - Returns: Roman numeric string for the chord in a scale. - public func romanNumeric(for scale: Scale) -> String? { - guard let chordIndex = scale.keys.firstIndex(of: key) - else { return nil } - - var roman = "" - switch chordIndex { - case 0: roman = "I" - case 1: roman = "II" - case 2: roman = "III" - case 3: roman = "IV" - case 4: roman = "V" - case 5: roman = "VI" - case 6: roman = "VII" - default: return nil - } - - // Check if minor - if type.third == .minor { - roman = roman.lowercased() - } - // Check if sixth - if type.sixth != nil { - roman = "\(roman)6" - } - // Check if augmented - if type.fifth == .augmented { - roman = "\(roman)+" - } - // Check if diminished - if type.fifth == .diminished { - roman = "\(roman)°" - } - // Check if sevent - if type.seventh != nil, (type.extensions == nil || type.extensions?.isEmpty == true) { - roman = "\(roman)7" - } - // Check if extended - if let extensions = type.extensions, - let last = extensions.sorted(by: { $0.type.rawValue < $1.type.rawValue }).last { - roman = "\(roman)\(last.type.rawValue)" + /// Type of the chord. + public var type: ChordType + /// Root key of the chord. + public var key: Key + /// Inversion index of the chord. + public private(set) var inversion: Int + + /// Initilizes chord with root note and type. + /// + /// - Parameters: + /// - key: Root key of the chord. + /// - type: Tyoe of the chord. + public init(type: ChordType, key: Key, inversion: Int = 0) { + self.type = type + self.key = key + self.inversion = inversion + } + + /// Types of notes in chord. + public var keys: [Key] { + return pitches(octave: 1).map({ $0.key }) + } + + /// Possible inversions of the chord. + public var inversions: [Chord] { + return [Int](0 ..< keys.count).map({ Chord(type: type, key: key, inversion: $0) }) + } + + /// Notation of the chord. + public var notation: String { + let inversionNotation = inversion > 0 && inversion < keys.count ? "/\(keys[0])" : "" + return "\(key)\(type.notation)\(inversionNotation)" + } + + /// Generates notes of the chord for octave. + /// + /// - Parameter octave: Octave of the root note for the build chord from. + /// - Returns: Generates notes of the chord. + public func pitches(octave: Int) -> [Pitch] { + var intervals = type.intervals + for _ in 0 ..< inversion { + intervals = intervals.shifted + } + + let root = Pitch(key: key, octave: octave) + let invertedPitches = intervals.map({ root + $0 }) + return invertedPitches + .enumerated() + .map({ index, item in + index < type.intervals.count - inversion ? item : Pitch(key: item.key, octave: item.octave + 1) + }) + } + + /// Generates notes of the chord for octave range. + /// + /// - Parameter octaves: Octaves of the root note to build chord from. + /// - Returns: Generates notes of the chord. + public func pitches(octaves: [Int]) -> [Pitch] { + return octaves.flatMap({ pitches(octave: $0) }).sorted(by: { $0.rawValue < $1.rawValue }) + } + + /// Returns the roman numeric string for a chord. + /// + /// - Parameter scale: The scale that the chord in. + /// - Returns: Roman numeric string for the chord in a scale. + public func romanNumeric(for scale: Scale) -> String? { + guard let chordIndex = scale.keys.firstIndex(of: key) + else { return nil } + + var roman = "" + switch chordIndex { + case 0: roman = "I" + case 1: roman = "II" + case 2: roman = "III" + case 3: roman = "IV" + case 4: roman = "V" + case 5: roman = "VI" + case 6: roman = "VII" + default: return nil + } + + // Check if minor + if type.third == .minor { + roman = roman.lowercased() + } + // Check if sixth + if type.sixth != nil { + roman = "\(roman)6" + } + // Check if augmented + if type.fifth == .augmented { + roman = "\(roman)+" + } + // Check if diminished + if type.fifth == .diminished { + roman = "\(roman)°" + } + // Check if sevent + if type.seventh != nil, (type.extensions == nil || type.extensions?.isEmpty == true) { + roman = "\(roman)7" + } + // Check if extended + if let extensions = type.extensions, + let last = extensions.sorted(by: { $0.type.rawValue < $1.type.rawValue }).last { + roman = "\(roman)\(last.type.rawValue)" + } + // Check if inverted + if inversion > 0 { + roman = "\(roman)/\(inversion)" + } + + return roman } - // Check if inverted - if inversion > 0 { - roman = "\(roman)/\(inversion)" + + // MARK: CustomStringConvertible + + /// Description of the chord. + public var description: String { + let inversionNotation = inversion > 0 ? " \(inversion). Inversion" : "" + return "\(key) \(type)\(inversionNotation)" } - - return roman - } - - // MARK: CustomStringConvertible - - /// Description of the chord. - public var description: String { - let inversionNotation = inversion > 0 ? " \(inversion). Inversion" : "" - return "\(key) \(type)\(inversionNotation)" - } } // MARK: - Extensions extension Array { - internal var shifted: Array { - guard let firstElement = first else { return self } - var arr = self - arr.removeFirst() - arr.append(firstElement) - return arr - } + internal var shifted: Array { + guard let firstElement = first else { return self } + var arr = self + arr.removeFirst() + arr.append(firstElement) + return arr + } } diff --git a/Sources/MusicTheory/ChordProgression.swift b/Sources/MusicTheory/ChordProgression.swift index 783a080..7c80075 100644 --- a/Sources/MusicTheory/ChordProgression.swift +++ b/Sources/MusicTheory/ChordProgression.swift @@ -11,212 +11,212 @@ import Foundation /// A struct for storing custom progressions. public struct CustomChordProgression: Codable, CustomStringConvertible { - /// Name of the progression. - public var name: String - /// Chord progression with `ChordProgresion.custom` type. - public var progression: ChordProgression - - // MARK: CustomStringConvertible - - public var description: String { - return name - } + /// Name of the progression. + public var name: String + /// Chord progression with `ChordProgresion.custom` type. + public var progression: ChordProgression + + // MARK: CustomStringConvertible + + public var description: String { + return name + } } /// A node of chord progression in intervals. public enum ChordProgressionNode: Int, CustomStringConvertible, Codable, Hashable { - /// First-degree node - case i - /// Second-degree node - case ii - /// Third-degree node - case iii - /// Fourth-degree node - case iv - /// Fifth-degree node - case v - /// Sixth-degree node - case vi - /// Seventh-degree node - case vii - - /// Meaningful next nodes, useful for a recommendation engine. - public var next: [ChordProgressionNode] { - switch self { - case .i: - return [.i, .ii, .iii, .iv, .v, .vi, .vii] - case .ii: - return [.v, .iii, .vi, .vii] - case .iii: - return [.ii, .iv, .vi] - case .iv: - return [.i, .iii, .v, .vii] - case .v: - return [.i] - case .vi: - return [.ii, .iv] - case .vii: - return [.vi] + /// First-degree node + case i + /// Second-degree node + case ii + /// Third-degree node + case iii + /// Fourth-degree node + case iv + /// Fifth-degree node + case v + /// Sixth-degree node + case vi + /// Seventh-degree node + case vii + + /// Meaningful next nodes, useful for a recommendation engine. + public var next: [ChordProgressionNode] { + switch self { + case .i: + return [.i, .ii, .iii, .iv, .v, .vi, .vii] + case .ii: + return [.v, .iii, .vi, .vii] + case .iii: + return [.ii, .iv, .vi] + case .iv: + return [.i, .iii, .v, .vii] + case .v: + return [.i] + case .vi: + return [.ii, .iv] + case .vii: + return [.vi] + } } - } - - /// All nodes. - public static let all: [ChordProgressionNode] = [.i, .ii, .iii, .iv, .v, .vi, .vii] - - // MARK: CustomStringConvertible - - /// Returns roman numeric string of the node. - public var description: String { - switch self { - case .i: return "I" - case .ii: return "II" - case .iii: return "III" - case .iv: return "IV" - case .v: return "V" - case .vi: return "VI" - case .vii: return "VII" + + /// All nodes. + public static let all: [ChordProgressionNode] = [.i, .ii, .iii, .iv, .v, .vi, .vii] + + // MARK: CustomStringConvertible + + /// Returns roman numeric string of the node. + public var description: String { + switch self { + case .i: return "I" + case .ii: return "II" + case .iii: return "III" + case .iv: return "IV" + case .v: return "V" + case .vi: return "VI" + case .vii: return "VII" + } } - } } /// Chord progression enum that you can create hard-coded and custom progressions. public struct ChordProgression: CustomStringConvertible, Codable, Hashable { - /// All nodes from first to seventh. - public static let allNodes = ChordProgression(nodes: [.i, .ii, .iii, .iv, .v, .vi, .vii]) - /// I - V - VI - IV progression. - public static let i_v_vi_iv = ChordProgression(nodes: [.i, .v, .vi, .iv]) - /// VI - V - IV - V progression. - public static let vi_v_iv_v = ChordProgression(nodes: [.vi, .v, .iv, .v]) - /// I - VI - IV - V progression. - public static let i_vi_iv_v = ChordProgression(nodes: [.i, .vi, .iv, .v]) - /// I - IV - VI - V progression. - public static let i_iv_vi_v = ChordProgression(nodes: [.i, .iv, .vi, .v]) - /// I - V - IV - V progression. - public static let i_v_iv_v = ChordProgression(nodes: [.i, .v, .iv, .v]) - /// VI - II - V - I progression. - public static let vi_ii_v_i = ChordProgression(nodes: [.vi, .ii, .v, .i]) - /// I - VI - II - V progression. - public static let i_vi_ii_v = ChordProgression(nodes: [.i, .vi, .ii, .v]) - /// I - IV - II - V progression. - public static let i_iv_ii_v = ChordProgression(nodes: [.i, .iv, .ii, .v]) - /// VI - IV - I - V progression. - public static let vi_iv_i_v = ChordProgression(nodes: [.vi, .iv, .i, .v]) - /// I - VI - III - VII progression. - public static let i_vi_iii_vii = ChordProgression(nodes: [.i, .vi, .iii, .vii]) - /// VI - V - IV - III progression. - public static let vi_v_iv_iii = ChordProgression(nodes: [.vi, .v, .iv, .iii]) - /// I - V - VI - III - IV - I - IV - V progression. - public static let i_v_vi_iii_iv_i_iv_v = ChordProgression(nodes: [.i, .v, .vi, .iii, .iv, .i, .iv, .v]) - /// IV - I - V - IV progression. - public static let iv_i_v_iv = ChordProgression(nodes: [.iv, .i, .v, .iv]) - /// I - II - VI - IV progression. - public static let i_ii_vi_iv = ChordProgression(nodes: [.i, .ii, .vi, .iv]) - /// I - III - VI - IV progression. - public static let i_iii_vi_iv = ChordProgression(nodes: [.i, .iii, .vi, .iv]) - /// I - V - II - IV progression. - public static let i_v_ii_iv = ChordProgression(nodes: [.i, .v, .ii, .iv]) - /// II - IV - I - V progression. - public static let ii_iv_i_v = ChordProgression(nodes: [.ii, .iv, .i, .v]) - - public let nodes: [ChordProgressionNode] - - /// Initilizes the chord progression with its nodes. - /// - /// - Parameter nodes: Nodes of the chord progression. - public init(nodes: [ChordProgressionNode]) { - self.nodes = nodes - } - - /// All hard-coded chord progressions. - public static var all: [ChordProgression] { - return [ - .allNodes, - .i_v_vi_iv, - .vi_v_iv_v, - .i_vi_iv_v, - .i_iv_vi_v, - .i_v_iv_v, - .vi_ii_v_i, - .i_vi_ii_v, - .i_iv_ii_v, - .vi_iv_i_v, - .i_vi_iii_vii, - .vi_v_iv_iii, - .i_v_vi_iii_iv_i_iv_v, - .iv_i_v_iv, - .i_ii_vi_iv, - .i_iii_vi_iv, - .i_v_ii_iv, - .ii_iv_i_v, - ] - } - - /// Generates chord progression for a `Scale` with `Scale.HarmonicField` and optionally inverted chords. - /// - /// - Parameters: - /// - scale: Scale of the chords going to be generated. - /// - harmonicField: Harmonic field of the chords going to be generated. - /// - inversion: Inversion of the chords going to be generated. - /// - Returns: Returns all possible chords for a scale. Returns nil if the chord is not generated for particular `ChordProgressionNode`. - public func chords(for scale: Scale, harmonicField: Scale.HarmonicField, inversion: Int = 0) -> [Chord?] { - let indices = nodes.map({ $0.rawValue }) - let harmonics = scale.harmonicField(for: harmonicField, inversion: inversion) - var chords = [Chord?]() - for index in indices { - if index < harmonics.count { - chords.append(harmonics[index]) - } + /// All nodes from first to seventh. + public static let allNodes = ChordProgression(nodes: [.i, .ii, .iii, .iv, .v, .vi, .vii]) + /// I - V - VI - IV progression. + public static let i_v_vi_iv = ChordProgression(nodes: [.i, .v, .vi, .iv]) + /// VI - V - IV - V progression. + public static let vi_v_iv_v = ChordProgression(nodes: [.vi, .v, .iv, .v]) + /// I - VI - IV - V progression. + public static let i_vi_iv_v = ChordProgression(nodes: [.i, .vi, .iv, .v]) + /// I - IV - VI - V progression. + public static let i_iv_vi_v = ChordProgression(nodes: [.i, .iv, .vi, .v]) + /// I - V - IV - V progression. + public static let i_v_iv_v = ChordProgression(nodes: [.i, .v, .iv, .v]) + /// VI - II - V - I progression. + public static let vi_ii_v_i = ChordProgression(nodes: [.vi, .ii, .v, .i]) + /// I - VI - II - V progression. + public static let i_vi_ii_v = ChordProgression(nodes: [.i, .vi, .ii, .v]) + /// I - IV - II - V progression. + public static let i_iv_ii_v = ChordProgression(nodes: [.i, .iv, .ii, .v]) + /// VI - IV - I - V progression. + public static let vi_iv_i_v = ChordProgression(nodes: [.vi, .iv, .i, .v]) + /// I - VI - III - VII progression. + public static let i_vi_iii_vii = ChordProgression(nodes: [.i, .vi, .iii, .vii]) + /// VI - V - IV - III progression. + public static let vi_v_iv_iii = ChordProgression(nodes: [.vi, .v, .iv, .iii]) + /// I - V - VI - III - IV - I - IV - V progression. + public static let i_v_vi_iii_iv_i_iv_v = ChordProgression(nodes: [.i, .v, .vi, .iii, .iv, .i, .iv, .v]) + /// IV - I - V - IV progression. + public static let iv_i_v_iv = ChordProgression(nodes: [.iv, .i, .v, .iv]) + /// I - II - VI - IV progression. + public static let i_ii_vi_iv = ChordProgression(nodes: [.i, .ii, .vi, .iv]) + /// I - III - VI - IV progression. + public static let i_iii_vi_iv = ChordProgression(nodes: [.i, .iii, .vi, .iv]) + /// I - V - II - IV progression. + public static let i_v_ii_iv = ChordProgression(nodes: [.i, .v, .ii, .iv]) + /// II - IV - I - V progression. + public static let ii_iv_i_v = ChordProgression(nodes: [.ii, .iv, .i, .v]) + + public let nodes: [ChordProgressionNode] + + /// Initilizes the chord progression with its nodes. + /// + /// - Parameter nodes: Nodes of the chord progression. + public init(nodes: [ChordProgressionNode]) { + self.nodes = nodes } - return chords - } - - // MARK: CustomStringConvertible - - /// Returns the chord progression name. - public var description: String { - if self == ChordProgression.allNodes { - return "All" + + /// All hard-coded chord progressions. + public static var all: [ChordProgression] { + return [ + .allNodes, + .i_v_vi_iv, + .vi_v_iv_v, + .i_vi_iv_v, + .i_iv_vi_v, + .i_v_iv_v, + .vi_ii_v_i, + .i_vi_ii_v, + .i_iv_ii_v, + .vi_iv_i_v, + .i_vi_iii_vii, + .vi_v_iv_iii, + .i_v_vi_iii_iv_i_iv_v, + .iv_i_v_iv, + .i_ii_vi_iv, + .i_iii_vi_iv, + .i_v_ii_iv, + .ii_iv_i_v, + ] + } + + /// Generates chord progression for a `Scale` with `Scale.HarmonicField` and optionally inverted chords. + /// + /// - Parameters: + /// - scale: Scale of the chords going to be generated. + /// - harmonicField: Harmonic field of the chords going to be generated. + /// - inversion: Inversion of the chords going to be generated. + /// - Returns: Returns all possible chords for a scale. Returns nil if the chord is not generated for particular `ChordProgressionNode`. + public func chords(for scale: Scale, harmonicField: Scale.HarmonicField, inversion: Int = 0) -> [Chord?] { + let indices = nodes.map({ $0.rawValue }) + let harmonics = scale.harmonicField(for: harmonicField, inversion: inversion) + var chords = [Chord?]() + for index in indices { + if index < harmonics.count { + chords.append(harmonics[index]) + } + } + return chords + } + + // MARK: CustomStringConvertible + + /// Returns the chord progression name. + public var description: String { + if self == ChordProgression.allNodes { + return "All" + } + return nodes.map({ "\($0)" }).joined(separator: " - ") + } + + // MARK: Codable + + /// Codable protocol `CodingKey`s + /// + /// - nodes: Coding key for the nodes. + private enum CodingKeys: String, CodingKey { + case nodes + } + + /// Initilizes chord progression with a `Decoder`. + /// + /// - Parameter decoder: The decoder. + /// - Throws: Throws error if can not decodes. + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + let nodes = try values.decode([ChordProgressionNode].self, forKey: .nodes) + self = ChordProgression(nodes: nodes) + } + + /// Encodes the chord progression. + /// + /// - Parameter encoder: The encoder. + /// - Throws: Throws error if can not encodes. + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(nodes, forKey: .nodes) + } + + // MARK: Hashable + + public func hash(into hasher: inout Hasher) { + hasher.combine(nodes) + } + + // MARK: Equatable + + public static func == (lhs: ChordProgression, rhs: ChordProgression) -> Bool { + return lhs.hashValue == rhs.hashValue } - return nodes.map({ "\($0)" }).joined(separator: " - ") - } - - // MARK: Codable - - /// Codable protocol `CodingKey`s - /// - /// - nodes: Coding key for the nodes. - private enum CodingKeys: String, CodingKey { - case nodes - } - - /// Initilizes chord progression with a `Decoder`. - /// - /// - Parameter decoder: The decoder. - /// - Throws: Throws error if can not decodes. - public init(from decoder: Decoder) throws { - let values = try decoder.container(keyedBy: CodingKeys.self) - let nodes = try values.decode([ChordProgressionNode].self, forKey: .nodes) - self = ChordProgression(nodes: nodes) - } - - /// Encodes the chord progression. - /// - /// - Parameter encoder: The encoder. - /// - Throws: Throws error if can not encodes. - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(nodes, forKey: .nodes) - } - - // MARK: Hashable - - public func hash(into hasher: inout Hasher) { - hasher.combine(nodes) - } - - // MARK: Equatable - - public static func == (lhs: ChordProgression, rhs: ChordProgression) -> Bool { - return lhs.hashValue == rhs.hashValue - } } diff --git a/Sources/MusicTheory/HarmonicFunctions.swift b/Sources/MusicTheory/HarmonicFunctions.swift index 8166a77..a25fd2a 100644 --- a/Sources/MusicTheory/HarmonicFunctions.swift +++ b/Sources/MusicTheory/HarmonicFunctions.swift @@ -1,6 +1,6 @@ // // HarmonicFunctions.swift -// +// // // Created by Cem Olcay on 29/06/2020. // Copyright © 2017 cemolcay. All rights reserved. @@ -13,81 +13,81 @@ import Foundation /// Represents harmonic functions in music theory. public enum HarmonicFunctionType: Int, Codable, CaseIterable { - /// First interval/chord. I in roman numeral. - case tonic - /// Second interval/chord. II in roman numeral. - case supertonic - /// Third interval/chord. III in roman numeral. - case mediant - /// Fourth interval/chord. IV in roman numeral. - case subdominant - /// Fifth interval/chord. V in roman numeral. - case dominant - /// Sixth interval/chord. VI in roman numeral. - case submediant - /// Seventh interval/chord. VII in roman numeral. - case leading - - /// Represents tonic prolongation functions. - public static let tonicProlongationFunctions: [HarmonicFunctionType] = [.mediant, .submediant] - - /// Represents the pre dominant functions. - public static let predominantFunctions: [HarmonicFunctionType] = [.supertonic, .submediant] - - /// Represents the dominant functions - public static let dominantFunctions: [HarmonicFunctionType] = [.dominant, .leading] - - /// Represents the possible direction from any harmonic function. - public var direction: [HarmonicFunctionType] { - switch self { - case .tonic: - return HarmonicFunctionType.allCases - case .supertonic: - return HarmonicFunctionType.dominantFunctions - case .mediant: - return HarmonicFunctionType.predominantFunctions - case .subdominant: - return [.supertonic] + HarmonicFunctionType.dominantFunctions - case .dominant: - return [.tonic] - case .submediant: - return HarmonicFunctionType.predominantFunctions - case .leading: - return [.tonic, .dominant, .submediant] + /// First interval/chord. I in roman numeral. + case tonic + /// Second interval/chord. II in roman numeral. + case supertonic + /// Third interval/chord. III in roman numeral. + case mediant + /// Fourth interval/chord. IV in roman numeral. + case subdominant + /// Fifth interval/chord. V in roman numeral. + case dominant + /// Sixth interval/chord. VI in roman numeral. + case submediant + /// Seventh interval/chord. VII in roman numeral. + case leading + + /// Represents tonic prolongation functions. + public static let tonicProlongationFunctions: [HarmonicFunctionType] = [.mediant, .submediant] + + /// Represents the pre dominant functions. + public static let predominantFunctions: [HarmonicFunctionType] = [.supertonic, .submediant] + + /// Represents the dominant functions + public static let dominantFunctions: [HarmonicFunctionType] = [.dominant, .leading] + + /// Represents the possible direction from any harmonic function. + public var direction: [HarmonicFunctionType] { + switch self { + case .tonic: + return HarmonicFunctionType.allCases + case .supertonic: + return HarmonicFunctionType.dominantFunctions + case .mediant: + return HarmonicFunctionType.predominantFunctions + case .subdominant: + return [.supertonic] + HarmonicFunctionType.dominantFunctions + case .dominant: + return [.tonic] + case .submediant: + return HarmonicFunctionType.predominantFunctions + case .leading: + return [.tonic, .dominant, .submediant] + } } - } - - /// Returns the roman numeral string representation. - public var romanNumeral: String { - switch self { - case .tonic: return "I" - case .supertonic: return "II" - case .mediant: return "III" - case .subdominant: return "IV" - case .dominant: return "V" - case .submediant: return "VI" - case .leading: return "VII" + + /// Returns the roman numeral string representation. + public var romanNumeral: String { + switch self { + case .tonic: return "I" + case .supertonic: return "II" + case .mediant: return "III" + case .subdominant: return "IV" + case .dominant: return "V" + case .submediant: return "VI" + case .leading: return "VII" + } } - } } /// A struct for creating harmonic functions from a `Scale`. public struct HarmonicFunctions { - /// Scale of the harmonic function. - public let scale: Scale - - /// Initilize the harmonic functions for a scale. - /// - Parameter scale: The scale you want to create harmonic functions from. - public init(scale: Scale) { - self.scale = scale - } - - /// Returns the key of the scale's harmonic function. - /// - Parameter type: The harmonic function you want to get from the scale. - /// - Returns: Returns the key representing the harmonic function you want to get, if the scale has it. - public func harmonicFunction(for type: HarmonicFunctionType) -> Key? { - let keys = scale.keys - guard keys.count >= type.rawValue else { return nil } - return keys[type.rawValue] - } + /// Scale of the harmonic function. + public let scale: Scale + + /// Initilize the harmonic functions for a scale. + /// - Parameter scale: The scale you want to create harmonic functions from. + public init(scale: Scale) { + self.scale = scale + } + + /// Returns the key of the scale's harmonic function. + /// - Parameter type: The harmonic function you want to get from the scale. + /// - Returns: Returns the key representing the harmonic function you want to get, if the scale has it. + public func harmonicFunction(for type: HarmonicFunctionType) -> Key? { + let keys = scale.keys + guard keys.count >= type.rawValue else { return nil } + return keys[type.rawValue] + } } diff --git a/Sources/MusicTheory/Interval.swift b/Sources/MusicTheory/Interval.swift index 2f38666..70802e7 100644 --- a/Sources/MusicTheory/Interval.swift +++ b/Sources/MusicTheory/Interval.swift @@ -17,7 +17,7 @@ import Foundation /// - rhs: Right hand side of the equation. /// - Returns: Returns true if two `Interval`s are equal. public func == (lhs: Interval, rhs: Interval) -> Bool { - return lhs.semitones == rhs.semitones + return lhs.semitones == rhs.semitones } /// Checks the equality of two `Interval`s in terms of their quality, degree and semitones. @@ -27,213 +27,213 @@ public func == (lhs: Interval, rhs: Interval) -> Bool { /// - rhs: Right hand side of the equation. /// - Returns: Returns true if two `Interval`s are equal. public func === (lhs: Interval, rhs: Interval) -> Bool { - return lhs.quality == rhs.quality && rhs.degree == rhs.degree && lhs.semitones == rhs.semitones + return lhs.quality == rhs.quality && rhs.degree == rhs.degree && lhs.semitones == rhs.semitones } /// Defines the interval between `Pitch`es in semitones. public struct Interval: Codable, Hashable, CustomStringConvertible { - /// Quality type of the interval. - public enum Quality: Int, Codable, Hashable, CaseIterable, CustomStringConvertible { - /// Diminished - case diminished - /// Perfect - case perfect - /// Minor. - case minor - /// Major. - case major - /// Augmented. - case augmented - + /// Quality type of the interval. + public enum Quality: Int, Codable, Hashable, CaseIterable, CustomStringConvertible { + /// Diminished + case diminished + /// Perfect + case perfect + /// Minor. + case minor + /// Major. + case major + /// Augmented. + case augmented + + // MARK: CustomStringConvertible + + /// Returns the notation of the interval quality. + public var notation: String { + switch self { + case .diminished: return "d" + case .perfect: return "P" + case .minor: return "m" + case .major: return "M" + case .augmented: return "A" + } + } + + /// Returns the name of the interval quality. + public var description: String { + switch self { + case .diminished: return "Diminished" + case .perfect: return "Perfect" + case .minor: return "Minor" + case .major: return "Major" + case .augmented: return "Augmented" + } + } + } + + /// Quality of the interval. + public var quality: Quality + /// Degree of the interval. + public var degree: Int + /// Semitones interval affect on a pitch. + public var semitones: Int + + /// Initilizes the interval with its quality, degree and semitones. + /// + /// - Parameters: + /// - quality: Quality of the interval. + /// - degree: Degree of the interval. + /// - semitones: Semitones of the interval. + public init(quality: Quality, degree: Int, semitones: Int) { + self.quality = quality + self.degree = degree + self.semitones = semitones + } + + /// Unison. + public static let P1 = Interval(quality: .perfect, degree: 1, semitones: 0) + /// Perfect fourth. + public static let P4 = Interval(quality: .perfect, degree: 4, semitones: 5) + /// Perfect fifth. + public static let P5 = Interval(quality: .perfect, degree: 5, semitones: 7) + /// Octave. + public static let P8 = Interval(quality: .perfect, degree: 8, semitones: 12) + /// Perfect eleventh. + public static let P11 = Interval(quality: .perfect, degree: 11, semitones: 17) + /// Perfect twelfth. + public static let P12 = Interval(quality: .perfect, degree: 12, semitones: 19) + /// Perfect fifteenth, double octave. + public static let P15 = Interval(quality: .perfect, degree: 15, semitones: 24) + + /// Minor second. + public static let m2 = Interval(quality: .minor, degree: 2, semitones: 1) + /// Minor third. + public static let m3 = Interval(quality: .minor, degree: 3, semitones: 3) + /// Minor sixth. + public static let m6 = Interval(quality: .minor, degree: 6, semitones: 8) + /// Minor seventh. + public static let m7 = Interval(quality: .minor, degree: 7, semitones: 10) + /// Minor ninth. + public static let m9 = Interval(quality: .minor, degree: 9, semitones: 13) + /// Minor tenth. + public static let m10 = Interval(quality: .minor, degree: 10, semitones: 15) + /// Minor thirteenth. + public static let m13 = Interval(quality: .minor, degree: 13, semitones: 20) + /// Minor fourteenth. + public static let m14 = Interval(quality: .minor, degree: 14, semitones: 22) + + /// Major second. + public static let M2 = Interval(quality: .major, degree: 2, semitones: 2) + /// Major third. + public static let M3 = Interval(quality: .major, degree: 3, semitones: 4) + /// Major sixth. + public static let M6 = Interval(quality: .major, degree: 6, semitones: 9) + /// Major seventh. + public static let M7 = Interval(quality: .major, degree: 7, semitones: 11) + /// Major ninth. + public static let M9 = Interval(quality: .major, degree: 9, semitones: 14) + /// Major tenth. + public static let M10 = Interval(quality: .major, degree: 10, semitones: 16) + /// Major thirteenth. + public static let M13 = Interval(quality: .major, degree: 13, semitones: 21) + /// Major fourteenth. + public static let M14 = Interval(quality: .major, degree: 14, semitones: 23) + + /// Diminished first. + public static let d1 = Interval(quality: .diminished, degree: 1, semitones: -1) + /// Diminished second. + public static let d2 = Interval(quality: .diminished, degree: 2, semitones: 0) + /// Diminished third. + public static let d3 = Interval(quality: .diminished, degree: 3, semitones: 2) + /// Diminished fourth. + public static let d4 = Interval(quality: .diminished, degree: 4, semitones: 4) + /// Diminished fifth. + public static let d5 = Interval(quality: .diminished, degree: 5, semitones: 6) + /// Diminished sixth. + public static let d6 = Interval(quality: .diminished, degree: 6, semitones: 7) + /// Diminished seventh. + public static let d7 = Interval(quality: .diminished, degree: 7, semitones: 9) + /// Diminished eighth. + public static let d8 = Interval(quality: .diminished, degree: 8, semitones: 11) + /// Diminished ninth. + public static let d9 = Interval(quality: .diminished, degree: 9, semitones: 12) + /// Diminished tenth. + public static let d10 = Interval(quality: .diminished, degree: 10, semitones: 14) + /// Diminished eleventh. + public static let d11 = Interval(quality: .diminished, degree: 11, semitones: 16) + /// Diminished twelfth. + public static let d12 = Interval(quality: .diminished, degree: 12, semitones: 18) + /// Diminished thirteenth. + public static let d13 = Interval(quality: .diminished, degree: 13, semitones: 19) + /// Diminished fourteenth. + public static let d14 = Interval(quality: .diminished, degree: 14, semitones: 21) + /// Diminished fifteenth. + public static let d15 = Interval(quality: .diminished, degree: 15, semitones: 23) + + /// Augmented first. + public static let A1 = Interval(quality: .augmented, degree: 1, semitones: 1) + /// Augmented second. + public static let A2 = Interval(quality: .augmented, degree: 2, semitones: 3) + /// Augmented third. + public static let A3 = Interval(quality: .augmented, degree: 3, semitones: 5) + /// Augmented fourth. + public static let A4 = Interval(quality: .augmented, degree: 4, semitones: 6) + /// Augmented fifth. + public static let A5 = Interval(quality: .augmented, degree: 5, semitones: 8) + /// Augmented sixth. + public static let A6 = Interval(quality: .augmented, degree: 6, semitones: 10) + /// Augmented seventh. + public static let A7 = Interval(quality: .augmented, degree: 7, semitones: 12) + /// Augmented octave. + public static let A8 = Interval(quality: .augmented, degree: 8, semitones: 13) + /// Augmented ninth. + public static let A9 = Interval(quality: .augmented, degree: 9, semitones: 15) + /// Augmented tenth. + public static let A10 = Interval(quality: .augmented, degree: 10, semitones: 17) + /// Augmented eleventh. + public static let A11 = Interval(quality: .augmented, degree: 11, semitones: 18) + /// Augmented twelfth. + public static let A12 = Interval(quality: .augmented, degree: 12, semitones: 20) + /// Augmented thirteenth. + public static let A13 = Interval(quality: .augmented, degree: 13, semitones: 22) + /// Augmented fourteenth. + public static let A14 = Interval(quality: .augmented, degree: 14, semitones: 24) + /// Augmented fifteenth. + public static let A15 = Interval(quality: .augmented, degree: 15, semitones: 25) + + /// All pre-defined intervals in a static array. You can filter it out with qualities, degrees or semitones. + public static let all: [Interval] = [ + .P1, .P4, .P5, .P8, .P11, .P12, .P15, + .m2, .m3, .m6, .m7, .m9, .m10, .m13, .m14, + .M2, .M3, .M6, .M7, .M9, .M10, .M13, .M14, + .d1, .d2, .d3, .d4, .d5, .d6, .d7, .d8, .d9, .d10, .d11, .d12, .d13, .d14, .d15, + .A1, .A2, .A3, .A4, .A5, .A6, .A7, .A8, .A9, .A10, .A11, .A12, .A13, .A14, .A15, + ] + // MARK: CustomStringConvertible - - /// Returns the notation of the interval quality. + + /// Returns the notation of the interval. public var notation: String { - switch self { - case .diminished: return "d" - case .perfect: return "P" - case .minor: return "m" - case .major: return "M" - case .augmented: return "A" - } + return "\(quality.notation)\(degree)" } - - /// Returns the name of the interval quality. + + /// Returns the name of the interval. public var description: String { - switch self { - case .diminished: return "Diminished" - case .perfect: return "Perfect" - case .minor: return "Minor" - case .major: return "Major" - case .augmented: return "Augmented" - } + var formattedDegree = "\(degree)" + + if #available(OSX 10.11, iOS 9.0, *) { + let formatter = NumberFormatter() + formatter.numberStyle = .ordinal + formattedDegree = formatter.string(from: NSNumber(integerLiteral: degree)) ?? formattedDegree + } + + return "\(quality) \(formattedDegree)" } - } - - /// Quality of the interval. - public var quality: Quality - /// Degree of the interval. - public var degree: Int - /// Semitones interval affect on a pitch. - public var semitones: Int - - /// Initilizes the interval with its quality, degree and semitones. - /// - /// - Parameters: - /// - quality: Quality of the interval. - /// - degree: Degree of the interval. - /// - semitones: Semitones of the interval. - public init(quality: Quality, degree: Int, semitones: Int) { - self.quality = quality - self.degree = degree - self.semitones = semitones - } - - /// Unison. - public static let P1 = Interval(quality: .perfect, degree: 1, semitones: 0) - /// Perfect fourth. - public static let P4 = Interval(quality: .perfect, degree: 4, semitones: 5) - /// Perfect fifth. - public static let P5 = Interval(quality: .perfect, degree: 5, semitones: 7) - /// Octave. - public static let P8 = Interval(quality: .perfect, degree: 8, semitones: 12) - /// Perfect eleventh. - public static let P11 = Interval(quality: .perfect, degree: 11, semitones: 17) - /// Perfect twelfth. - public static let P12 = Interval(quality: .perfect, degree: 12, semitones: 19) - /// Perfect fifteenth, double octave. - public static let P15 = Interval(quality: .perfect, degree: 15, semitones: 24) - - /// Minor second. - public static let m2 = Interval(quality: .minor, degree: 2, semitones: 1) - /// Minor third. - public static let m3 = Interval(quality: .minor, degree: 3, semitones: 3) - /// Minor sixth. - public static let m6 = Interval(quality: .minor, degree: 6, semitones: 8) - /// Minor seventh. - public static let m7 = Interval(quality: .minor, degree: 7, semitones: 10) - /// Minor ninth. - public static let m9 = Interval(quality: .minor, degree: 9, semitones: 13) - /// Minor tenth. - public static let m10 = Interval(quality: .minor, degree: 10, semitones: 15) - /// Minor thirteenth. - public static let m13 = Interval(quality: .minor, degree: 13, semitones: 20) - /// Minor fourteenth. - public static let m14 = Interval(quality: .minor, degree: 14, semitones: 22) - - /// Major second. - public static let M2 = Interval(quality: .major, degree: 2, semitones: 2) - /// Major third. - public static let M3 = Interval(quality: .major, degree: 3, semitones: 4) - /// Major sixth. - public static let M6 = Interval(quality: .major, degree: 6, semitones: 9) - /// Major seventh. - public static let M7 = Interval(quality: .major, degree: 7, semitones: 11) - /// Major ninth. - public static let M9 = Interval(quality: .major, degree: 9, semitones: 14) - /// Major tenth. - public static let M10 = Interval(quality: .major, degree: 10, semitones: 16) - /// Major thirteenth. - public static let M13 = Interval(quality: .major, degree: 13, semitones: 21) - /// Major fourteenth. - public static let M14 = Interval(quality: .major, degree: 14, semitones: 23) - - /// Diminished first. - public static let d1 = Interval(quality: .diminished, degree: 1, semitones: -1) - /// Diminished second. - public static let d2 = Interval(quality: .diminished, degree: 2, semitones: 0) - /// Diminished third. - public static let d3 = Interval(quality: .diminished, degree: 3, semitones: 2) - /// Diminished fourth. - public static let d4 = Interval(quality: .diminished, degree: 4, semitones: 4) - /// Diminished fifth. - public static let d5 = Interval(quality: .diminished, degree: 5, semitones: 6) - /// Diminished sixth. - public static let d6 = Interval(quality: .diminished, degree: 6, semitones: 7) - /// Diminished seventh. - public static let d7 = Interval(quality: .diminished, degree: 7, semitones: 9) - /// Diminished eighth. - public static let d8 = Interval(quality: .diminished, degree: 8, semitones: 11) - /// Diminished ninth. - public static let d9 = Interval(quality: .diminished, degree: 9, semitones: 12) - /// Diminished tenth. - public static let d10 = Interval(quality: .diminished, degree: 10, semitones: 14) - /// Diminished eleventh. - public static let d11 = Interval(quality: .diminished, degree: 11, semitones: 16) - /// Diminished twelfth. - public static let d12 = Interval(quality: .diminished, degree: 12, semitones: 18) - /// Diminished thirteenth. - public static let d13 = Interval(quality: .diminished, degree: 13, semitones: 19) - /// Diminished fourteenth. - public static let d14 = Interval(quality: .diminished, degree: 14, semitones: 21) - /// Diminished fifteenth. - public static let d15 = Interval(quality: .diminished, degree: 15, semitones: 23) - - /// Augmented first. - public static let A1 = Interval(quality: .augmented, degree: 1, semitones: 1) - /// Augmented second. - public static let A2 = Interval(quality: .augmented, degree: 2, semitones: 3) - /// Augmented third. - public static let A3 = Interval(quality: .augmented, degree: 3, semitones: 5) - /// Augmented fourth. - public static let A4 = Interval(quality: .augmented, degree: 4, semitones: 6) - /// Augmented fifth. - public static let A5 = Interval(quality: .augmented, degree: 5, semitones: 8) - /// Augmented sixth. - public static let A6 = Interval(quality: .augmented, degree: 6, semitones: 10) - /// Augmented seventh. - public static let A7 = Interval(quality: .augmented, degree: 7, semitones: 12) - /// Augmented octave. - public static let A8 = Interval(quality: .augmented, degree: 8, semitones: 13) - /// Augmented ninth. - public static let A9 = Interval(quality: .augmented, degree: 9, semitones: 15) - /// Augmented tenth. - public static let A10 = Interval(quality: .augmented, degree: 10, semitones: 17) - /// Augmented eleventh. - public static let A11 = Interval(quality: .augmented, degree: 11, semitones: 18) - /// Augmented twelfth. - public static let A12 = Interval(quality: .augmented, degree: 12, semitones: 20) - /// Augmented thirteenth. - public static let A13 = Interval(quality: .augmented, degree: 13, semitones: 22) - /// Augmented fourteenth. - public static let A14 = Interval(quality: .augmented, degree: 14, semitones: 24) - /// Augmented fifteenth. - public static let A15 = Interval(quality: .augmented, degree: 15, semitones: 25) - - /// All pre-defined intervals in a static array. You can filter it out with qualities, degrees or semitones. - public static let all: [Interval] = [ - .P1, .P4, .P5, .P8, .P11, .P12, .P15, - .m2, .m3, .m6, .m7, .m9, .m10, .m13, .m14, - .M2, .M3, .M6, .M7, .M9, .M10, .M13, .M14, - .d1, .d2, .d3, .d4, .d5, .d6, .d7, .d8, .d9, .d10, .d11, .d12, .d13, .d14, .d15, - .A1, .A2, .A3, .A4, .A5, .A6, .A7, .A8, .A9, .A10, .A11, .A12, .A13, .A14, .A15, - ] - - // MARK: CustomStringConvertible - - /// Returns the notation of the interval. - public var notation: String { - return "\(quality.notation)\(degree)" - } - - /// Returns the name of the interval. - public var description: String { - var formattedDegree = "\(degree)" - - if #available(OSX 10.11, iOS 9.0, *) { - let formatter = NumberFormatter() - formatter.numberStyle = .ordinal - formattedDegree = formatter.string(from: NSNumber(integerLiteral: degree)) ?? formattedDegree + + // MARK: Hashable + + public func hash(into hasher: inout Hasher) { + hasher.combine(quality) + hasher.combine(degree) + hasher.combine(semitones) } - - return "\(quality) \(formattedDegree)" - } - - // MARK: Hashable - - public func hash(into hasher: inout Hasher) { - hasher.combine(quality) - hasher.combine(degree) - hasher.combine(semitones) - } } diff --git a/Sources/MusicTheory/Key.swift b/Sources/MusicTheory/Key.swift index cd7c22a..3aa5282 100644 --- a/Sources/MusicTheory/Key.swift +++ b/Sources/MusicTheory/Key.swift @@ -17,13 +17,13 @@ import Foundation /// - rhs: Right hand side of the equation. /// - Returns: Returns the equation value. public func == (lhs: Key, rhs: Key) -> Bool { - let lhsMod = (lhs.type.rawValue + lhs.accidental.rawValue) % 12 - let normalizedLhs = lhsMod < 0 ? (12 + lhsMod) : lhsMod - - let rhsMod = (rhs.type.rawValue + rhs.accidental.rawValue) % 12 - let normalizedRhs = rhsMod < 0 ? (12 + rhsMod) : rhsMod - - return normalizedLhs == normalizedRhs + let lhsMod = (lhs.type.rawValue + lhs.accidental.rawValue) % 12 + let normalizedLhs = lhsMod < 0 ? (12 + lhsMod) : lhsMod + + let rhsMod = (rhs.type.rawValue + rhs.accidental.rawValue) % 12 + let normalizedRhs = rhsMod < 0 ? (12 + rhsMod) : rhsMod + + return normalizedLhs == normalizedRhs } /// Checks if two `Key` types are equal in terms of their type and accidental values. @@ -33,219 +33,219 @@ public func == (lhs: Key, rhs: Key) -> Bool { /// - rhs: Right hand side of the equation. /// - Returns: Returns the equation value. public func === (lhs: Key, rhs: Key) -> Bool { - return lhs.type == rhs.type && lhs.accidental == rhs.accidental + return lhs.type == rhs.type && lhs.accidental == rhs.accidental } /// Represents the keys that notes and pitches are based on. public struct Key: Codable, Equatable, Hashable, ExpressibleByStringLiteral, CustomStringConvertible { - /// Base pitch of the key without accidentals. Accidentals will take account in the parent struct, `Key`. Integer values are based on C = 0 on western chromatic scale. - public enum KeyType: Int, Codable, Equatable, Hashable, ExpressibleByStringLiteral, CustomStringConvertible { - /// C key. - case c = 0 - /// D key. - case d = 2 - /// E key. - case e = 4 - /// F key. - case f = 5 - /// G key. - case g = 7 - /// A key. - case a = 9 - /// B key. - case b = 11 - - /// Returns all members of the `KeyType`. - public static let all: [KeyType] = [.c, .d, .e, .f, .g, .a, .b] - - /// Returns neighbour `KeyType` at `distance` away. Works on both directions. - /// Use negative distance value for going on left direction, positive distance value for going on right direction. - /// This function iterates the `KeyType.all` array circullar to find the target KeyType. - /// - /// - Parameter distance: Target KeyType distance. Zero is self. - /// - Returns: Returns the neighbouring KeyType distance away. - public func key(at distance: Int) -> KeyType { - guard let index = KeyType.all.firstIndex(of: self) - else { return self } - - let normalizedDistance = (distance + index) % KeyType.all.count - let keyIndex = normalizedDistance < 0 ? (KeyType.all.count + normalizedDistance) : normalizedDistance - return KeyType.all[keyIndex] - } - - /// Calculates the distance of two `KeyType`s. - /// - /// - Parameter keyType: Target `KeyType` you want to compare. - /// - Returns: Returns the integer value of distance in terms of their array index values. - public func distance(from keyType: KeyType) -> Int { - guard let index = KeyType.all.firstIndex(of: self), - let targetIndex = KeyType.all.firstIndex(of: keyType) - else { return 0 } - return targetIndex - index + /// Base pitch of the key without accidentals. Accidentals will take account in the parent struct, `Key`. Integer values are based on C = 0 on western chromatic scale. + public enum KeyType: Int, Codable, Equatable, Hashable, ExpressibleByStringLiteral, CustomStringConvertible { + /// C key. + case c = 0 + /// D key. + case d = 2 + /// E key. + case e = 4 + /// F key. + case f = 5 + /// G key. + case g = 7 + /// A key. + case a = 9 + /// B key. + case b = 11 + + /// Returns all members of the `KeyType`. + public static let all: [KeyType] = [.c, .d, .e, .f, .g, .a, .b] + + /// Returns neighbour `KeyType` at `distance` away. Works on both directions. + /// Use negative distance value for going on left direction, positive distance value for going on right direction. + /// This function iterates the `KeyType.all` array circullar to find the target KeyType. + /// + /// - Parameter distance: Target KeyType distance. Zero is self. + /// - Returns: Returns the neighbouring KeyType distance away. + public func key(at distance: Int) -> KeyType { + guard let index = KeyType.all.firstIndex(of: self) + else { return self } + + let normalizedDistance = (distance + index) % KeyType.all.count + let keyIndex = normalizedDistance < 0 ? (KeyType.all.count + normalizedDistance) : normalizedDistance + return KeyType.all[keyIndex] + } + + /// Calculates the distance of two `KeyType`s. + /// + /// - Parameter keyType: Target `KeyType` you want to compare. + /// - Returns: Returns the integer value of distance in terms of their array index values. + public func distance(from keyType: KeyType) -> Int { + guard let index = KeyType.all.firstIndex(of: self), + let targetIndex = KeyType.all.firstIndex(of: keyType) + else { return 0 } + return targetIndex - index + } + + /// Calculates the octave difference for a neighbouring `KeyType` at given interval away higher or lower. + /// + /// - Parameters: + /// - interval: Interval you want to calculate octave difference. + /// - isHigher: You want to calculate interval higher or lower from current key. + /// - Returns: Returns the octave difference for a given interval higher or lower. + public func octaveDiff(for interval: Interval, isHigher: Bool) -> Int { + var diff = 0 + var currentKey = self + for _ in 0 ..< (interval.degree - 1) { + let next = currentKey.key(at: isHigher ? 1 : -1) + + if isHigher { + if currentKey == .b, next == .c { + diff += 1 + } + } else { + if currentKey == .c, next == .b { + diff -= 1 + } + } + + currentKey = next + } + return diff + } + + // MARK: ExpressibleByStringLiteral + + public typealias StringLiteralType = String + + /// Initilizes with a string. + /// + /// - Parameter value: String representation of type. + public init(stringLiteral value: KeyType.StringLiteralType) { + switch value.lowercased() { + case "a": self = .a + case "b": self = .b + case "c": self = .c + case "d": self = .d + case "e": self = .e + case "f": self = .f + case "g": self = .g + default: self = .c + } + } + + // MARK: CustomStringConvertible + + /// Returns the key notation. + public var description: String { + switch self { + case .c: return "C" + case .d: return "D" + case .e: return "E" + case .f: return "F" + case .g: return "G" + case .a: return "A" + case .b: return "B" + } + } } - - /// Calculates the octave difference for a neighbouring `KeyType` at given interval away higher or lower. + + /// Type of the key. + public var type: KeyType + + /// Accidental of the key. + public var accidental: Accidental + + /// All notes in an octave with sharp notes. + public static let keysWithSharps = [ + Key(type: .c, accidental: .natural), + Key(type: .c, accidental: .sharp), + Key(type: .d, accidental: .natural), + Key(type: .d, accidental: .sharp), + Key(type: .e, accidental: .natural), + Key(type: .f, accidental: .natural), + Key(type: .f, accidental: .sharp), + Key(type: .g, accidental: .natural), + Key(type: .g, accidental: .sharp), + Key(type: .a, accidental: .natural), + Key(type: .a, accidental: .sharp), + Key(type: .b, accidental: .natural), + ] + + /// All notes in an octave with flat notes. + public static let keysWithFlats = [ + Key(type: .c, accidental: .natural), + Key(type: .d, accidental: .flat), + Key(type: .d, accidental: .natural), + Key(type: .e, accidental: .flat), + Key(type: .e, accidental: .natural), + Key(type: .f, accidental: .natural), + Key(type: .g, accidental: .flat), + Key(type: .g, accidental: .natural), + Key(type: .a, accidental: .flat), + Key(type: .a, accidental: .natural), + Key(type: .b, accidental: .flat), + Key(type: .b, accidental: .natural), + ] + + /// All notes in an octave with both flat and sharp notes. + public static let allKeys = [ + Key(type: .c, accidental: .natural), + Key(type: .c, accidental: .sharp), + Key(type: .d, accidental: .flat), + Key(type: .d, accidental: .natural), + Key(type: .d, accidental: .sharp), + Key(type: .e, accidental: .flat), + Key(type: .e, accidental: .natural), + Key(type: .f, accidental: .natural), + Key(type: .f, accidental: .sharp), + Key(type: .g, accidental: .flat), + Key(type: .g, accidental: .natural), + Key(type: .g, accidental: .sharp), + Key(type: .a, accidental: .flat), + Key(type: .a, accidental: .natural), + Key(type: .a, accidental: .sharp), + Key(type: .b, accidental: .flat), + Key(type: .b, accidental: .natural), + ] + + /// Initilizes the key with its type and accidental. /// /// - Parameters: - /// - interval: Interval you want to calculate octave difference. - /// - isHigher: You want to calculate interval higher or lower from current key. - /// - Returns: Returns the octave difference for a given interval higher or lower. - public func octaveDiff(for interval: Interval, isHigher: Bool) -> Int { - var diff = 0 - var currentKey = self - for _ in 0 ..< (interval.degree - 1) { - let next = currentKey.key(at: isHigher ? 1 : -1) - - if isHigher { - if currentKey == .b, next == .c { - diff += 1 - } - } else { - if currentKey == .c, next == .b { - diff -= 1 - } - } - - currentKey = next - } - return diff + /// - type: The type of the key. + /// - accidental: Accidental of the key. Defaults natural. + public init(type: KeyType, accidental: Accidental = .natural) { + self.type = type + self.accidental = accidental } - + // MARK: ExpressibleByStringLiteral - + public typealias StringLiteralType = String - + /// Initilizes with a string. /// /// - Parameter value: String representation of type. - public init(stringLiteral value: KeyType.StringLiteralType) { - switch value.lowercased() { - case "a": self = .a - case "b": self = .b - case "c": self = .c - case "d": self = .d - case "e": self = .e - case "f": self = .f - case "g": self = .g - default: self = .c - } + public init(stringLiteral value: Key.StringLiteralType) { + var keyType = KeyType.c + var accidental = Accidental.natural + let pattern = "([A-Ga-g])([#♯♭b]*)" + let regex = try? NSRegularExpression(pattern: pattern, options: []) + if let regex = regex, + let match = regex.firstMatch(in: value, options: [], range: NSRange(0 ..< value.count)), + let keyTypeRange = Range(match.range(at: 1), in: value), + let accidentalRange = Range(match.range(at: 2), in: value), + match.numberOfRanges == 3 { + // Set key type + keyType = KeyType(stringLiteral: String(value[keyTypeRange])) + // Set accidental + accidental = Accidental(stringLiteral: String(value[accidentalRange])) + } + + self = Key(type: keyType, accidental: accidental) } - + // MARK: CustomStringConvertible - - /// Returns the key notation. + + /// Returns the key notation with its type and accidental, if has any. public var description: String { - switch self { - case .c: return "C" - case .d: return "D" - case .e: return "E" - case .f: return "F" - case .g: return "G" - case .a: return "A" - case .b: return "B" - } - } - } - - /// Type of the key. - public var type: KeyType - - /// Accidental of the key. - public var accidental: Accidental - - /// All notes in an octave with sharp notes. - public static let keysWithSharps = [ - Key(type: .c, accidental: .natural), - Key(type: .c, accidental: .sharp), - Key(type: .d, accidental: .natural), - Key(type: .d, accidental: .sharp), - Key(type: .e, accidental: .natural), - Key(type: .f, accidental: .natural), - Key(type: .f, accidental: .sharp), - Key(type: .g, accidental: .natural), - Key(type: .g, accidental: .sharp), - Key(type: .a, accidental: .natural), - Key(type: .a, accidental: .sharp), - Key(type: .b, accidental: .natural), - ] - - /// All notes in an octave with flat notes. - public static let keysWithFlats = [ - Key(type: .c, accidental: .natural), - Key(type: .d, accidental: .flat), - Key(type: .d, accidental: .natural), - Key(type: .e, accidental: .flat), - Key(type: .e, accidental: .natural), - Key(type: .f, accidental: .natural), - Key(type: .g, accidental: .flat), - Key(type: .g, accidental: .natural), - Key(type: .a, accidental: .flat), - Key(type: .a, accidental: .natural), - Key(type: .b, accidental: .flat), - Key(type: .b, accidental: .natural), - ] - - /// All notes in an octave with both flat and sharp notes. - public static let allKeys = [ - Key(type: .c, accidental: .natural), - Key(type: .c, accidental: .sharp), - Key(type: .d, accidental: .flat), - Key(type: .d, accidental: .natural), - Key(type: .d, accidental: .sharp), - Key(type: .e, accidental: .flat), - Key(type: .e, accidental: .natural), - Key(type: .f, accidental: .natural), - Key(type: .f, accidental: .sharp), - Key(type: .g, accidental: .flat), - Key(type: .g, accidental: .natural), - Key(type: .g, accidental: .sharp), - Key(type: .a, accidental: .flat), - Key(type: .a, accidental: .natural), - Key(type: .a, accidental: .sharp), - Key(type: .b, accidental: .flat), - Key(type: .b, accidental: .natural), - ] - - /// Initilizes the key with its type and accidental. - /// - /// - Parameters: - /// - type: The type of the key. - /// - accidental: Accidental of the key. Defaults natural. - public init(type: KeyType, accidental: Accidental = .natural) { - self.type = type - self.accidental = accidental - } - - // MARK: ExpressibleByStringLiteral - - public typealias StringLiteralType = String - - /// Initilizes with a string. - /// - /// - Parameter value: String representation of type. - public init(stringLiteral value: Key.StringLiteralType) { - var keyType = KeyType.c - var accidental = Accidental.natural - let pattern = "([A-Ga-g])([#♯♭b]*)" - let regex = try? NSRegularExpression(pattern: pattern, options: []) - if let regex = regex, - let match = regex.firstMatch(in: value, options: [], range: NSRange(0 ..< value.count)), - let keyTypeRange = Range(match.range(at: 1), in: value), - let accidentalRange = Range(match.range(at: 2), in: value), - match.numberOfRanges == 3 { - // Set key type - keyType = KeyType(stringLiteral: String(value[keyTypeRange])) - // Set accidental - accidental = Accidental(stringLiteral: String(value[accidentalRange])) + return "\(type)\(accidental)" } - - self = Key(type: keyType, accidental: accidental) - } - - // MARK: CustomStringConvertible - - /// Returns the key notation with its type and accidental, if has any. - public var description: String { - return "\(type)\(accidental)" - } } diff --git a/Sources/MusicTheory/NoteValue.swift b/Sources/MusicTheory/NoteValue.swift index 08763df..39ce181 100644 --- a/Sources/MusicTheory/NoteValue.swift +++ b/Sources/MusicTheory/NoteValue.swift @@ -20,61 +20,61 @@ public struct NoteValueType: Codable, Hashable, CustomStringConvertible { public var description: String - /// Sixteen bar notes. - public static let sixteenBars = NoteValueType(beats: 16.0, description: "16 Bars") - /// Eigth bar notes. - public static let eigthBars = NoteValueType(beats: 8.0, description: "8 Bars") - /// Four bar notes. - public static let fourBars = NoteValueType(beats: 4.0, description: "4 Bars") - /// Two bar notes. - public static let twoBars = NoteValueType(beats: 2.0, description: "2 Bars") - /// One bar note. - public static let oneBar = NoteValueType(beats: 1.0, description: "1 Bar") - /// Two whole notes. - public static let doubleWhole = NoteValueType(beats: 2.0 / 1.0, description: "2/1") - /// Whole note. - public static let whole = NoteValueType(beats: 1.0 / 1.0, description: "1/1") - /// Half note. - public static let half = NoteValueType(beats: 1.0 / 2.0, description: "1/2") - /// Quarter note. - public static let quarter = NoteValueType(beats: 1.0 / 4.0, description: "1/4") - /// Eighth note. - public static let eighth = NoteValueType(beats: 1.0 / 8.0, description: "1/8") - /// Sixteenth note. - public static let sixteenth = NoteValueType(beats: 1.0 / 16.0, description: "1/16") - /// Thirtysecond note. - public static let thirtysecond = NoteValueType(beats: 1.0 / 32.0, description: "1/32") - /// Sixtyfourth note. - public static let sixtyfourth = NoteValueType(beats: 1.0 / 64.0, description: "1/64") + /// Sixteen bar notes. + public static let sixteenBars = NoteValueType(beats: 16.0, description: "16 Bars") + /// Eigth bar notes. + public static let eigthBars = NoteValueType(beats: 8.0, description: "8 Bars") + /// Four bar notes. + public static let fourBars = NoteValueType(beats: 4.0, description: "4 Bars") + /// Two bar notes. + public static let twoBars = NoteValueType(beats: 2.0, description: "2 Bars") + /// One bar note. + public static let oneBar = NoteValueType(beats: 1.0, description: "1 Bar") + /// Two whole notes. + public static let doubleWhole = NoteValueType(beats: 2.0 / 1.0, description: "2/1") + /// Whole note. + public static let whole = NoteValueType(beats: 1.0 / 1.0, description: "1/1") + /// Half note. + public static let half = NoteValueType(beats: 1.0 / 2.0, description: "1/2") + /// Quarter note. + public static let quarter = NoteValueType(beats: 1.0 / 4.0, description: "1/4") + /// Eighth note. + public static let eighth = NoteValueType(beats: 1.0 / 8.0, description: "1/8") + /// Sixteenth note. + public static let sixteenth = NoteValueType(beats: 1.0 / 16.0, description: "1/16") + /// Thirtysecond note. + public static let thirtysecond = NoteValueType(beats: 1.0 / 32.0, description: "1/32") + /// Sixtyfourth note. + public static let sixtyfourth = NoteValueType(beats: 1.0 / 64.0, description: "1/64") - public static let all: [NoteValueType] = [ - .sixteenBars, .eigthBars, .fourBars, .twoBars, .oneBar, - .half, .quarter, .eighth, .sixteenth, .thirtysecond, .sixtyfourth - ] + public static let all: [NoteValueType] = [ + .sixteenBars, .eigthBars, .fourBars, .twoBars, .oneBar, + .half, .quarter, .eighth, .sixteenth, .thirtysecond, .sixtyfourth + ] } // MARK: - NoteModifier /// Defines the length of a `NoteValue` public enum NoteModifier: Double, Codable, CaseIterable, CustomStringConvertible { - /// No additional length. - case `default` = 1 - /// Adds half of its own value. - case dotted = 1.5 - /// Three notes of the same value. - case triplet = 0.6667 - /// Five of the indicated note value total the duration normally occupied by four. - case quintuplet = 0.8 - - /// The string representation of the modifier. - public var description: String { - switch self { - case .default: return "" - case .dotted: return "D" - case .triplet: return "T" - case .quintuplet: return "Q" + /// No additional length. + case `default` = 1 + /// Adds half of its own value. + case dotted = 1.5 + /// Three notes of the same value. + case triplet = 0.6667 + /// Five of the indicated note value total the duration normally occupied by four. + case quintuplet = 0.8 + + /// The string representation of the modifier. + public var description: String { + switch self { + case .default: return "" + case .dotted: return "D" + case .triplet: return "T" + case .quintuplet: return "Q" + } } - } } // MARK: - NoteValue @@ -91,23 +91,23 @@ public func / (noteValue: NoteValue, noteValueType: NoteValueType) -> Double { /// Defines the duration of a note beatwise. public struct NoteValue: Codable, CustomStringConvertible { - /// Type that represents the duration of note. - public var type: NoteValueType - /// Modifier for `NoteType` that modifies the duration. - public var modifier: NoteModifier - - /// Initilize the NoteValue with its type and optional modifier. - /// - /// - Parameters: - /// - type: Type of note value that represents note duration. - /// - modifier: Modifier of note value. Defaults `default`. - public init(type: NoteValueType, modifier: NoteModifier = .default) { - self.type = type - self.modifier = modifier - } - - /// Returns the string representation of the note value. - public var description: String { - return "\(type)\(modifier)" - } + /// Type that represents the duration of note. + public var type: NoteValueType + /// Modifier for `NoteType` that modifies the duration. + public var modifier: NoteModifier + + /// Initilize the NoteValue with its type and optional modifier. + /// + /// - Parameters: + /// - type: Type of note value that represents note duration. + /// - modifier: Modifier of note value. Defaults `default`. + public init(type: NoteValueType, modifier: NoteModifier = .default) { + self.type = type + self.modifier = modifier + } + + /// Returns the string representation of the note value. + public var description: String { + return "\(type)\(modifier)" + } } diff --git a/Sources/MusicTheory/Pitch.swift b/Sources/MusicTheory/Pitch.swift index a629ffe..7a215c0 100644 --- a/Sources/MusicTheory/Pitch.swift +++ b/Sources/MusicTheory/Pitch.swift @@ -17,16 +17,16 @@ import Foundation /// - rhs: Target `Interval`. /// - Returns: Returns new pitch above target interval from target pitch. public func + (lhs: Pitch, rhs: Interval) -> Pitch { - let degree = rhs.degree - 1 - let targetKeyType = lhs.key.type.key(at: degree) - let targetPitch = lhs + rhs.semitones - let targetOctave = lhs.octave + lhs.key.type.octaveDiff(for: rhs, isHigher: true) - - // Convert pitch - var convertedPitch = Pitch(key: Key(type: targetKeyType), octave: targetOctave) - let diff = targetPitch.rawValue - convertedPitch.rawValue - convertedPitch.key.accidental = Accidental(integerLiteral: diff) - return convertedPitch + let degree = rhs.degree - 1 + let targetKeyType = lhs.key.type.key(at: degree) + let targetPitch = lhs + rhs.semitones + let targetOctave = lhs.octave + lhs.key.type.octaveDiff(for: rhs, isHigher: true) + + // Convert pitch + var convertedPitch = Pitch(key: Key(type: targetKeyType), octave: targetOctave) + let diff = targetPitch.rawValue - convertedPitch.rawValue + convertedPitch.key.accidental = Accidental(integerLiteral: diff) + return convertedPitch } /// Returns the pitch below target interval from target pitch. @@ -36,16 +36,16 @@ public func + (lhs: Pitch, rhs: Interval) -> Pitch { /// - rhs: Target `Interval`. /// - Returns: Returns new pitch below target interval from target pitch. public func - (lhs: Pitch, rhs: Interval) -> Pitch { - let degree = -(rhs.degree - 1) - let targetKeyType = lhs.key.type.key(at: degree) - let targetPitch = lhs - rhs.semitones - let targetOctave = lhs.octave + lhs.key.type.octaveDiff(for: rhs, isHigher: false) - - // Convert pitch - var convertedPitch = Pitch(key: Key(type: targetKeyType), octave: targetOctave) - let diff = targetPitch.rawValue - convertedPitch.rawValue - convertedPitch.key.accidental = Accidental(integerLiteral: diff) - return convertedPitch + let degree = -(rhs.degree - 1) + let targetKeyType = lhs.key.type.key(at: degree) + let targetPitch = lhs - rhs.semitones + let targetOctave = lhs.octave + lhs.key.type.octaveDiff(for: rhs, isHigher: false) + + // Convert pitch + var convertedPitch = Pitch(key: Key(type: targetKeyType), octave: targetOctave) + let diff = targetPitch.rawValue - convertedPitch.rawValue + convertedPitch.key.accidental = Accidental(integerLiteral: diff) + return convertedPitch } /// Calculates the interval between two pitches. @@ -56,41 +56,41 @@ public func - (lhs: Pitch, rhs: Interval) -> Pitch { /// - rhs: Right hand side of the equation. /// - Returns: `Intreval` between two pitches. You can get the halfsteps from interval as well. public func - (lhs: Pitch, rhs: Pitch) -> Interval { - let top = max(lhs, rhs) - let bottom = min(lhs, rhs) - let diff = top.rawValue - bottom.rawValue - - let bottomKeyIndex = Key.KeyType.all.firstIndex(of: bottom.key.type) ?? 0 - let topKeyIndex = Key.KeyType.all.firstIndex(of: top.key.type) ?? 0 - let degree = abs(topKeyIndex - bottomKeyIndex) + 1 - let isMajor = (degree == 2 || degree == 3 || degree == 6 || degree == 7) - - let majorScale = Scale(type: .major, key: bottom.key) - if majorScale.keys.contains(top.key) { // Major or Perfect - return Interval( - quality: isMajor ? .major : .perfect, - degree: degree, - semitones: diff - ) - } else { // Augmented, Diminished or Minor - if isMajor { - let majorPitch = bottom + Interval(quality: .major, degree: degree, semitones: diff) - let offset = top.rawValue - majorPitch.rawValue - return Interval( - quality: offset > 0 ? .augmented : .minor, - degree: degree, - semitones: diff - ) - } else { - let perfectPitch = bottom + Interval(quality: .perfect, degree: degree, semitones: diff) - let offset = top.rawValue - perfectPitch.rawValue - return Interval( - quality: offset > 0 ? .augmented : .diminished, - degree: degree, - semitones: diff - ) + let top = max(lhs, rhs) + let bottom = min(lhs, rhs) + let diff = top.rawValue - bottom.rawValue + + let bottomKeyIndex = Key.KeyType.all.firstIndex(of: bottom.key.type) ?? 0 + let topKeyIndex = Key.KeyType.all.firstIndex(of: top.key.type) ?? 0 + let degree = abs(topKeyIndex - bottomKeyIndex) + 1 + let isMajor = (degree == 2 || degree == 3 || degree == 6 || degree == 7) + + let majorScale = Scale(type: .major, key: bottom.key) + if majorScale.keys.contains(top.key) { // Major or Perfect + return Interval( + quality: isMajor ? .major : .perfect, + degree: degree, + semitones: diff + ) + } else { // Augmented, Diminished or Minor + if isMajor { + let majorPitch = bottom + Interval(quality: .major, degree: degree, semitones: diff) + let offset = top.rawValue - majorPitch.rawValue + return Interval( + quality: offset > 0 ? .augmented : .minor, + degree: degree, + semitones: diff + ) + } else { + let perfectPitch = bottom + Interval(quality: .perfect, degree: degree, semitones: diff) + let offset = top.rawValue - perfectPitch.rawValue + return Interval( + quality: offset > 0 ? .augmented : .diminished, + degree: degree, + semitones: diff + ) + } } - } } /// Calculates the `Pitch` above halfsteps. @@ -100,7 +100,7 @@ public func - (lhs: Pitch, rhs: Pitch) -> Interval { /// - halfstep: Halfsteps above. /// - Returns: Returns `Pitch` above halfsteps. public func + (pitch: Pitch, halfstep: Int) -> Pitch { - return Pitch(midiNote: pitch.rawValue + halfstep) + return Pitch(midiNote: pitch.rawValue + halfstep) } /// Calculates the `Pitch` below halfsteps. @@ -110,7 +110,7 @@ public func + (pitch: Pitch, halfstep: Int) -> Pitch { /// - halfstep: Halfsteps below. /// - Returns: Returns `Pitch` below halfsteps. public func - (pitch: Pitch, halfstep: Int) -> Pitch { - return Pitch(midiNote: pitch.rawValue - halfstep) + return Pitch(midiNote: pitch.rawValue - halfstep) } /// Compares the equality of two pitches by their MIDI note value. @@ -121,7 +121,7 @@ public func - (pitch: Pitch, halfstep: Int) -> Pitch { /// - right: Right handside `Pitch` to be compared. /// - Returns: Returns the bool value of comparisation of two pitches. public func == (left: Pitch, right: Pitch) -> Bool { - return left.rawValue == right.rawValue + return left.rawValue == right.rawValue } /// Compares the exact equality of two pitches by their keys and octaves. @@ -132,7 +132,7 @@ public func == (left: Pitch, right: Pitch) -> Bool { /// - right: Right handside `Pitch` to be compared. /// - Returns: Returns the bool value of comparisation of two pitches. public func === (left: Pitch, right: Pitch) -> Bool { - return left.key == right.key && left.octave == right.octave + return left.key == right.key && left.octave == right.octave } /// Compares two `Pitch`es in terms of their semitones. @@ -142,151 +142,151 @@ public func === (left: Pitch, right: Pitch) -> Bool { /// - rhs: Right hand side of the equation. /// - Returns: Returns true if left hand side `Pitch` lower than right hand side `Pitch`. public func < (lhs: Pitch, rhs: Pitch) -> Bool { - return lhs.rawValue < rhs.rawValue + return lhs.rawValue < rhs.rawValue } /// Pitch object with a `Key` and an octave. /// Could be initilized with MIDI note number and preferred accidental type. public struct Pitch: RawRepresentable, Codable, Hashable, Comparable, ExpressibleByIntegerLiteral, ExpressibleByStringLiteral, CustomStringConvertible { - /// Key of the pitch like C, D, A, B with accidentals. - public var key: Key - - /// Octave of the pitch. - /// In theory this must be zero or a positive integer. - /// But `Note` does not limit octave and calculates every possible octave including the negative ones. - public var octave: Int - - /// This function returns the nearest pitch to the given frequency in Hz. - /// - /// - Parameter frequency: The frequency in Hz - /// - Returns: The nearest pitch for given frequency - public static func nearest(frequency: Float) -> Pitch? { - let allPitches = Array((1 ... 7).map { octave -> [Pitch] in - Key.keysWithSharps.map { key -> Pitch in - Pitch(key: key, octave: octave) - } - }.joined()) - - var results = allPitches.map { pitch -> (pitch: Pitch, distance: Float) in - (pitch: pitch, distance: abs(pitch.frequency - frequency)) + /// Key of the pitch like C, D, A, B with accidentals. + public var key: Key + + /// Octave of the pitch. + /// In theory this must be zero or a positive integer. + /// But `Note` does not limit octave and calculates every possible octave including the negative ones. + public var octave: Int + + /// This function returns the nearest pitch to the given frequency in Hz. + /// + /// - Parameter frequency: The frequency in Hz + /// - Returns: The nearest pitch for given frequency + public static func nearest(frequency: Float) -> Pitch? { + let allPitches = Array((1 ... 7).map { octave -> [Pitch] in + Key.keysWithSharps.map { key -> Pitch in + Pitch(key: key, octave: octave) + } + }.joined()) + + var results = allPitches.map { pitch -> (pitch: Pitch, distance: Float) in + (pitch: pitch, distance: abs(pitch.frequency - frequency)) + } + + results.sort { $0.distance < $1.distance } + return results.first?.pitch } - - results.sort { $0.distance < $1.distance } - return results.first?.pitch - } - - /// Initilizes the `Pitch` with MIDI note number. - /// - /// - Parameter midiNote: Midi note in range of [0 - 127]. - /// - Parameter preferSharps: Make it true if preferred accidentals is sharps. Defaults true. - public init(midiNote: Int, preferSharps: Bool = true) { - octave = (midiNote / 12) - 1 - let keyIndex = midiNote % 12 - key = (preferSharps ? Key.keysWithSharps : Key.keysWithFlats)[circular: keyIndex] ?? "c" - } - - /// Initilizes the `Pitch` with `Key` and octave - /// - /// - Parameters: - /// - key: Key of the pitch. - /// - octave: Octave of the pitch. - public init(key: Key, octave: Int) { - self.key = key - self.octave = octave - } - - /// Calculates and returns the frequency of note on octave based on its location of piano keys. - /// Bases A4 note of 440Hz frequency standard. - public var frequency: Float { - let fn = powf(2.0, Float(rawValue - 69) / 12.0) - return fn * 440.0 - } - - // MARK: RawRepresentable - - public typealias RawValue = Int - - /// Returns midi note number. - /// In theory, this must be in range [0 - 127]. - /// But it does not limits the midi note value. - public var rawValue: Int { - let semitones = key.type.rawValue + key.accidental.rawValue - return semitones + ((octave + 1) * 12) - } - - /// Initilizes the pitch with an integer value that represents the MIDI note number of the pitch. - /// - /// - Parameter rawValue: MIDI note number of the pitch. - public init?(rawValue: Pitch.RawValue) { - self = Pitch(midiNote: rawValue) - } - - // MARK: ExpressibleByIntegerLiteral - - public typealias IntegerLiteralType = Int - - /// Initilizes the pitch with an integer value that represents the MIDI note number of the pitch. - /// - /// - Parameter value: MIDI note number of the pitch. - public init(integerLiteral value: Pitch.IntegerLiteralType) { - self = Pitch(midiNote: value) - } - - // MARK: ExpressibleByStringLiteral - - public typealias StringLiteralType = String - - /// Initilizes with a string. - /// - /// - Parameter value: String representation of type. - public init(stringLiteral value: Pitch.StringLiteralType) { - var keyType = Key.KeyType.c - var accidental = Accidental.natural - var octave = 0 - let pattern = "([A-Ga-g])([#♯♭b]*)(-?)(\\d+)" - let regex = try? NSRegularExpression(pattern: pattern, options: []) - if let regex = regex, - let match = regex.firstMatch(in: value, options: [], range: NSRange(0 ..< value.count)), - let keyTypeRange = Range(match.range(at: 1), in: value), - let accidentalRange = Range(match.range(at: 2), in: value), - let signRange = Range(match.range(at: 3), in: value), - let octaveRange = Range(match.range(at: 4), in: value), - match.numberOfRanges == 5 { - // key type - keyType = Key.KeyType(stringLiteral: String(value[keyTypeRange])) - // accidental - accidental = Accidental(stringLiteral: String(value[accidentalRange])) - // sign - let sign = String(value[signRange]) - // octave - octave = (Int(String(value[octaveRange])) ?? 0) * (sign == "-" ? -1 : 1) + + /// Initilizes the `Pitch` with MIDI note number. + /// + /// - Parameter midiNote: Midi note in range of [0 - 127]. + /// - Parameter preferSharps: Make it true if preferred accidentals is sharps. Defaults true. + public init(midiNote: Int, preferSharps: Bool = true) { + octave = (midiNote / 12) - 1 + let keyIndex = midiNote % 12 + key = (preferSharps ? Key.keysWithSharps : Key.keysWithFlats)[circular: keyIndex] ?? "c" + } + + /// Initilizes the `Pitch` with `Key` and octave + /// + /// - Parameters: + /// - key: Key of the pitch. + /// - octave: Octave of the pitch. + public init(key: Key, octave: Int) { + self.key = key + self.octave = octave + } + + /// Calculates and returns the frequency of note on octave based on its location of piano keys. + /// Bases A4 note of 440Hz frequency standard. + public var frequency: Float { + let fn = powf(2.0, Float(rawValue - 69) / 12.0) + return fn * 440.0 + } + + // MARK: RawRepresentable + + public typealias RawValue = Int + + /// Returns midi note number. + /// In theory, this must be in range [0 - 127]. + /// But it does not limits the midi note value. + public var rawValue: Int { + let semitones = key.type.rawValue + key.accidental.rawValue + return semitones + ((octave + 1) * 12) + } + + /// Initilizes the pitch with an integer value that represents the MIDI note number of the pitch. + /// + /// - Parameter rawValue: MIDI note number of the pitch. + public init?(rawValue: Pitch.RawValue) { + self = Pitch(midiNote: rawValue) + } + + // MARK: ExpressibleByIntegerLiteral + + public typealias IntegerLiteralType = Int + + /// Initilizes the pitch with an integer value that represents the MIDI note number of the pitch. + /// + /// - Parameter value: MIDI note number of the pitch. + public init(integerLiteral value: Pitch.IntegerLiteralType) { + self = Pitch(midiNote: value) + } + + // MARK: ExpressibleByStringLiteral + + public typealias StringLiteralType = String + + /// Initilizes with a string. + /// + /// - Parameter value: String representation of type. + public init(stringLiteral value: Pitch.StringLiteralType) { + var keyType = Key.KeyType.c + var accidental = Accidental.natural + var octave = 0 + let pattern = "([A-Ga-g])([#♯♭b]*)(-?)(\\d+)" + let regex = try? NSRegularExpression(pattern: pattern, options: []) + if let regex = regex, + let match = regex.firstMatch(in: value, options: [], range: NSRange(0 ..< value.count)), + let keyTypeRange = Range(match.range(at: 1), in: value), + let accidentalRange = Range(match.range(at: 2), in: value), + let signRange = Range(match.range(at: 3), in: value), + let octaveRange = Range(match.range(at: 4), in: value), + match.numberOfRanges == 5 { + // key type + keyType = Key.KeyType(stringLiteral: String(value[keyTypeRange])) + // accidental + accidental = Accidental(stringLiteral: String(value[accidentalRange])) + // sign + let sign = String(value[signRange]) + // octave + octave = (Int(String(value[octaveRange])) ?? 0) * (sign == "-" ? -1 : 1) + } + + self = Pitch(key: Key(type: keyType, accidental: accidental), octave: octave) + } + + // MARK: Hashable + + public func hash(into hasher: inout Hasher) { + hasher.combine(key) + hasher.combine(octave) + } + + // MARK: CustomStringConvertible + + /// Converts `Pitch` to string with its key and octave. + public var description: String { + return "\(key)\(octave)" } - - self = Pitch(key: Key(type: keyType, accidental: accidental), octave: octave) - } - - // MARK: Hashable - - public func hash(into hasher: inout Hasher) { - hasher.combine(key) - hasher.combine(octave) - } - - // MARK: CustomStringConvertible - - /// Converts `Pitch` to string with its key and octave. - public var description: String { - return "\(key)\(octave)" - } } extension Array { - /// An array subscript extension that returns the element from the positive or negative circular index. - subscript(circular index: Int) -> Element? { - guard count > 0 else { return nil } - let mod = index % count - let offset = index >= 0 ? 0 : count - let idx = mod == 0 ? 0 : mod + offset - return self[idx] - } + /// An array subscript extension that returns the element from the positive or negative circular index. + subscript(circular index: Int) -> Element? { + guard count > 0 else { return nil } + let mod = index % count + let offset = index >= 0 ? 0 : count + let idx = mod == 0 ? 0 : mod + offset + return self[idx] + } } diff --git a/Sources/MusicTheory/Scale.swift b/Sources/MusicTheory/Scale.swift index 365c5f2..b760a9d 100644 --- a/Sources/MusicTheory/Scale.swift +++ b/Sources/MusicTheory/Scale.swift @@ -17,135 +17,135 @@ import Foundation /// - right: Right handside of the equation. /// - Returns: Returns Bool value of equation of two given scales. public func == (left: Scale, right: Scale) -> Bool { - return left.key == right.key && left.type == right.type + return left.key == right.key && left.type == right.type } /// Scale object with `ScaleType` and scale's key of `NoteType`. /// Could calculate note sequences in [Note] format. public struct Scale: Hashable, Codable { - /// Type of the scale that has `interval` info. - public var type: ScaleType - /// Root key of the scale that will built onto. - public var key: Key - - /// Initilizes the scale with its type and key. - /// - /// - Parameters: - /// - type: Type of scale being initilized. - /// - key: Key of scale being initilized. - public init(type: ScaleType, key: Key) { - self.type = type - self.key = key - } - - /// Keys generated by the intervals of the scale. - public var keys: [Key] { - return pitches(octave: 1).map({ $0.key }) - } - - /// Generates `Pitch` array of scale in given octave. - /// - /// - Parameter octave: Octave value of notes in scale. - /// - Returns: Returns `Pitch` array of the scale in given octave. - public func pitches(octave: Int) -> [Pitch] { - return pitches(octaves: octave) - } - - /// Generates `Pitch` array of scale in given octaves. - /// - /// - Parameter octaves: Variadic value of octaves to generate pitches in scale. - /// - Returns: Returns `Pitch` array of the scale in given octaves. - public func pitches(octaves: Int...) -> [Pitch] { - return pitches(octaves: octaves) - } - - /// Generates `Pitch` array of scale in given octaves. - /// - /// - Parameter octaves: Array value of octaves to generate pitches in scale. - /// - Returns: Returns `Pitch` array of the scale in given octaves. - public func pitches(octaves: [Int]) -> [Pitch] { - var pitches = [Pitch]() - octaves.forEach({ octave in - let root = Pitch(key: key, octave: octave) - pitches += type.intervals.map({ root + $0 }) - }) - return pitches - } - + /// Type of the scale that has `interval` info. + public var type: ScaleType + /// Root key of the scale that will built onto. + public var key: Key + + /// Initilizes the scale with its type and key. + /// + /// - Parameters: + /// - type: Type of scale being initilized. + /// - key: Key of scale being initilized. + public init(type: ScaleType, key: Key) { + self.type = type + self.key = key + } + + /// Keys generated by the intervals of the scale. + public var keys: [Key] { + return pitches(octave: 1).map({ $0.key }) + } + + /// Generates `Pitch` array of scale in given octave. + /// + /// - Parameter octave: Octave value of notes in scale. + /// - Returns: Returns `Pitch` array of the scale in given octave. + public func pitches(octave: Int) -> [Pitch] { + return pitches(octaves: octave) + } + + /// Generates `Pitch` array of scale in given octaves. + /// + /// - Parameter octaves: Variadic value of octaves to generate pitches in scale. + /// - Returns: Returns `Pitch` array of the scale in given octaves. + public func pitches(octaves: Int...) -> [Pitch] { + return pitches(octaves: octaves) + } + + /// Generates `Pitch` array of scale in given octaves. + /// + /// - Parameter octaves: Array value of octaves to generate pitches in scale. + /// - Returns: Returns `Pitch` array of the scale in given octaves. + public func pitches(octaves: [Int]) -> [Pitch] { + var pitches = [Pitch]() + octaves.forEach({ octave in + let root = Pitch(key: key, octave: octave) + pitches += type.intervals.map({ root + $0 }) + }) + return pitches + } + // MARK: Hashable - - public func hash(into hasher: inout Hasher) { - hasher.combine(key) - hasher.combine(type) - } + + public func hash(into hasher: inout Hasher) { + hasher.combine(key) + hasher.combine(type) + } } extension Scale { - /// Stack of notes to generate chords for each note in the scale. - public enum HarmonicField: Int, Codable { - /// First, third and fifth degree notes builds a triad chord. - case triad - /// First, third, fifth and seventh notes builds a tetrad chord. - case tetrad - /// First, third, fifth, seventh and ninth notes builds a 9th chord. - case ninth - /// First, third, fifth, seventh, ninth and eleventh notes builds a 11th chord. - case eleventh - /// First, third, fifth, seventh, ninth, eleventh and thirteenth notes builds a 13th chord. - case thirteenth - - /// All possible harmonic fields constructed from. - public static let all: [HarmonicField] = [.triad, .tetrad, .ninth, .eleventh, .thirteenth] - } - - /// Generates chords for harmonic field of scale. - /// - /// - Parameter field: Type of chords you want to generate. - /// - Parameter inversion: Inversion degree of the chords. Defaults 0. - /// - Returns: Returns triads or tetrads of chord for each note in scale. - public func harmonicField(for field: HarmonicField, inversion: Int = 0) -> [Chord?] { - var chords = [Chord?]() - - // Extended notes for picking notes. - let octaves = [0, 1, 2, 3, 4] - let scalePitches = pitches(octaves: octaves) - - // Build chords for each note in scale. - for i in 0 ..< scalePitches.count / octaves.count { - var chordPitches = [Pitch]() - switch field { - case .triad: - chordPitches = [scalePitches[i], scalePitches[i + 2], scalePitches[i + 4]] - case .tetrad: - chordPitches = [scalePitches[i], scalePitches[i + 2], scalePitches[i + 4], scalePitches[i + 6]] - case .ninth: - chordPitches = [scalePitches[i], scalePitches[i + 2], scalePitches[i + 4], scalePitches[i + 6], scalePitches[i + 8]] - case .eleventh: - chordPitches = [scalePitches[i], scalePitches[i + 2], scalePitches[i + 4], scalePitches[i + 6], scalePitches[i + 8], scalePitches[i + 10]] - case .thirteenth: - chordPitches = [scalePitches[i], scalePitches[i + 2], scalePitches[i + 4], scalePitches[i + 6], scalePitches[i + 8], scalePitches[i + 10], scalePitches[i + 12]] - } - - // Build intervals - let root = chordPitches[0] - let intervals = chordPitches.map({ $0 - root }) - - // Build chord - if let chordType = ChordType(intervals: intervals) { - let chord = Chord(type: chordType, key: root.key, inversion: inversion) - chords.append(chord) - } else { - chords.append(nil) - } + /// Stack of notes to generate chords for each note in the scale. + public enum HarmonicField: Int, Codable { + /// First, third and fifth degree notes builds a triad chord. + case triad + /// First, third, fifth and seventh notes builds a tetrad chord. + case tetrad + /// First, third, fifth, seventh and ninth notes builds a 9th chord. + case ninth + /// First, third, fifth, seventh, ninth and eleventh notes builds a 11th chord. + case eleventh + /// First, third, fifth, seventh, ninth, eleventh and thirteenth notes builds a 13th chord. + case thirteenth + + /// All possible harmonic fields constructed from. + public static let all: [HarmonicField] = [.triad, .tetrad, .ninth, .eleventh, .thirteenth] + } + + /// Generates chords for harmonic field of scale. + /// + /// - Parameter field: Type of chords you want to generate. + /// - Parameter inversion: Inversion degree of the chords. Defaults 0. + /// - Returns: Returns triads or tetrads of chord for each note in scale. + public func harmonicField(for field: HarmonicField, inversion: Int = 0) -> [Chord?] { + var chords = [Chord?]() + + // Extended notes for picking notes. + let octaves = [0, 1, 2, 3, 4] + let scalePitches = pitches(octaves: octaves) + + // Build chords for each note in scale. + for i in 0 ..< scalePitches.count / octaves.count { + var chordPitches = [Pitch]() + switch field { + case .triad: + chordPitches = [scalePitches[i], scalePitches[i + 2], scalePitches[i + 4]] + case .tetrad: + chordPitches = [scalePitches[i], scalePitches[i + 2], scalePitches[i + 4], scalePitches[i + 6]] + case .ninth: + chordPitches = [scalePitches[i], scalePitches[i + 2], scalePitches[i + 4], scalePitches[i + 6], scalePitches[i + 8]] + case .eleventh: + chordPitches = [scalePitches[i], scalePitches[i + 2], scalePitches[i + 4], scalePitches[i + 6], scalePitches[i + 8], scalePitches[i + 10]] + case .thirteenth: + chordPitches = [scalePitches[i], scalePitches[i + 2], scalePitches[i + 4], scalePitches[i + 6], scalePitches[i + 8], scalePitches[i + 10], scalePitches[i + 12]] + } + + // Build intervals + let root = chordPitches[0] + let intervals = chordPitches.map({ $0 - root }) + + // Build chord + if let chordType = ChordType(intervals: intervals) { + let chord = Chord(type: chordType, key: root.key, inversion: inversion) + chords.append(chord) + } else { + chords.append(nil) + } + } + + return chords } - - return chords - } } extension Scale: CustomStringConvertible { - /// Converts `Scale` to string with its key and type. - public var description: String { - return "\(key) \(type): " + keys.map({ "\($0)" }).joined(separator: ", ") - } + /// Converts `Scale` to string with its key and type. + public var description: String { + return "\(key) \(type): " + keys.map({ "\($0)" }).joined(separator: ", ") + } } diff --git a/Sources/MusicTheory/ScaleType.swift b/Sources/MusicTheory/ScaleType.swift index cd0870d..1977e8e 100644 --- a/Sources/MusicTheory/ScaleType.swift +++ b/Sources/MusicTheory/ScaleType.swift @@ -11,515 +11,515 @@ import Foundation /// Represents scale by the intervals between note sequences. public struct ScaleType: Codable, Hashable, CustomStringConvertible { - /// Intervals of the scale. - public let intervals: [Interval] - /// Description of the scale. - public let description: String - - /// Initilize the scale with series of its intervals. - /// - /// - Parameters: - /// - intervals: Intervals of the scale. - /// - description: Description of the scale. - public init(intervals: [Interval], description: String) { - self.intervals = intervals - self.description = description - } - - // MARK: Hashable - - public func hash(into hasher: inout Hasher) { - hasher.combine(intervals) - } - - // MARK: Equatable - - /// Checks the equability between two `ScaleType`s by their intervals. - /// - /// - Parameters: - /// - left: Left handside of the equation. - /// - right: Right handside of the equation. - /// - Returns: Returns Bool value of equation of two given scale types. - public static func == (left: ScaleType, right: ScaleType) -> Bool { - return left.intervals == right.intervals && left.description == right.description - } - - // MARK: Codable - - /// Keys that conforms CodingKeys protocol to map properties. - private enum CodingKeys: String, CodingKey { - /// Halfstep property of `Interval`. - case intervals - /// Name of the scale. - case description - } - - /// Decodes struct with a decoder. - /// - /// - Parameter decoder: Decodes encoded struct. - /// - Throws: Tries to initlize struct with a decoder. - public init(from decoder: Decoder) throws { - let values = try decoder.container(keyedBy: CodingKeys.self) - let intervals = try values.decode([Interval].self, forKey: .intervals) - let description = try values.decode(String.self, forKey: .description) - self = ScaleType(intervals: intervals, description: description) - } - - /// Encodes struct with an ecoder. - /// - /// - Parameter encoder: Encodes struct. - /// - Throws: Tries to encode struct. - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(intervals, forKey: .intervals) - try container.encode(description, forKey: .description) - } + /// Intervals of the scale. + public let intervals: [Interval] + /// Description of the scale. + public let description: String + + /// Initilize the scale with series of its intervals. + /// + /// - Parameters: + /// - intervals: Intervals of the scale. + /// - description: Description of the scale. + public init(intervals: [Interval], description: String) { + self.intervals = intervals + self.description = description + } + + // MARK: Hashable + + public func hash(into hasher: inout Hasher) { + hasher.combine(intervals) + } + + // MARK: Equatable + + /// Checks the equability between two `ScaleType`s by their intervals. + /// + /// - Parameters: + /// - left: Left handside of the equation. + /// - right: Right handside of the equation. + /// - Returns: Returns Bool value of equation of two given scale types. + public static func == (left: ScaleType, right: ScaleType) -> Bool { + return left.intervals == right.intervals && left.description == right.description + } + + // MARK: Codable + + /// Keys that conforms CodingKeys protocol to map properties. + private enum CodingKeys: String, CodingKey { + /// Halfstep property of `Interval`. + case intervals + /// Name of the scale. + case description + } + + /// Decodes struct with a decoder. + /// + /// - Parameter decoder: Decodes encoded struct. + /// - Throws: Tries to initlize struct with a decoder. + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + let intervals = try values.decode([Interval].self, forKey: .intervals) + let description = try values.decode(String.self, forKey: .description) + self = ScaleType(intervals: intervals, description: description) + } + + /// Encodes struct with an ecoder. + /// + /// - Parameter encoder: Encodes struct. + /// - Throws: Tries to encode struct. + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(intervals, forKey: .intervals) + try container.encode(description, forKey: .description) + } } extension ScaleType { - /// Major scale. - public static let major = ScaleType(intervals: ScaleType.ionian.intervals, description: "Major") - - /// Minor scale. - public static let minor = ScaleType(intervals: ScaleType.aeolian.intervals, description: "Minor") - - /// Harmonic minor scale. - public static let harmonicMinor = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .m6, .M7], description: "Harmonic Minor") - - /// Melodic minor scale. - public static let melodicMinor = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .M6, .M7], description: "Melodic Minor") - - /// Pentatonic major scale. - public static let pentatonicMajor = ScaleType(intervals: [.P1, .M2, .M3, .P5, .M6], description: "Pentatonic Major") - - /// Pentatonic minor scale. - public static let pentatonicMinor = ScaleType(intervals: [.P1, .m3, .P4, .P5, .m7], description: "Pentatonic Minor") - - /// Pentatonic blues scale. - public static let pentatonicBlues = ScaleType(intervals: [.P1, .m3, .P4, .d5, .P5, .m7], description: "Pentatonic Blues") - - /// Pentatonic neutral scale. - public static let pentatonicNeutral = ScaleType(intervals: [.P1, .M2, .P4, .P5, .m7], description: "Pentatonic Neutral") - - /// Ionian scale. - public static let ionian = ScaleType(intervals: [.P1, .M2, .M3, .P4, .P5, .M6, .M7], description: "Ionian") - - /// Aeolian scale. - public static let aeolian = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .m6, .m7], description: "Aeolian") - - /// Dorian scale. - public static let dorian = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .M6, .m7], description: "Dorian") - - /// Mixolydian scale. - public static let mixolydian = ScaleType(intervals: [.P1, .M2, .M3, .P4, .P5, .M6, .m7], description: "Mixolydian") - - /// Phrygian scale. - public static let phrygian = ScaleType(intervals: [.P1, .m2, .m3, .P4, .P5, .m6, .m7], description: "Phrygian") - - /// Lydian scale. - public static let lydian = ScaleType(intervals: [.P1, .M2, .M3, .A4, .P5, .M6, .M7], description: "Lydian") - - /// Locrian scale. - public static let locrian = ScaleType(intervals: [.P1, .m2, .m3, .P4, .d5, .m6, .m7], description: "Locrian") - - /// Half diminished scale. - public static let halfDiminished = ScaleType(intervals: [.P1, .m2, .m3, .M3, .d5, .P5, .M6, .m7], description: "Half Diminished") - - /// Whole diminished scale. - public static let wholeDiminished = ScaleType(intervals: [.P1, .M2, .m3, .P4, .d5, .m6, .M6, .M7], description: "Whole Diminished") - - /// Whole scale. - public static let whole = ScaleType(intervals: [.P1, .M2, .M3, .d5, .m6, .m7], description: "Whole") - - /// Augmented scale. - public static let augmented = ScaleType(intervals: [.m3, .M3, .P5, .m6, .M7], description: "Augmented") - - /// Chromatic scale. - public static let chromatic = ScaleType(intervals: [.P1, .m2, .M2, .m3, .M3, .P4, .d5, .P5, .m6, .M6, .m7, .M7], description: "Chromatic") - - /// Roumanian minor scale. - public static let romanianMinor = ScaleType(intervals: [.P1, .M2, .m3, .d5, .P5, .M6, .m7], description: "Romanian Minor") - - /// Spanish gypsy scale. - public static let spanishGypsy = ScaleType(intervals: [.P1, .m2, .M3, .P4, .P5, .m6, .m7], description: "Spanish Gypsy") - - /// Diatonic scale. - public static let diatonic = ScaleType(intervals: [.P1, .M2, .M3, .P5, .M6], description: "Diatonic") - - /// Dobule harmonic scale. - public static let doubleHarmonic = ScaleType(intervals: [.P1, .m2, .M3, .P4, .P5, .m6, .M7], description: "Double Harmonic") - - /// Eight tone spanish scale. - public static let eightToneSpanish = ScaleType(intervals: [.P1, .m2, .m3, .M3, .P4, .d5, .m6, .m7], description: "Eight Tone Spanish") - - /// Enigmatic scale. - public static let enigmatic = ScaleType(intervals: [.P1, .m2, .M3, .A4, .A5, .A6, .M7], description: "Enigmatic") - - /// Leading whole tone scale. - public static let leadingWholeTone = ScaleType(intervals: [.P1, .M2, .M3, .d5, .m6, .M6, .m7], description: "Leading Whole Tone") - - /// Lydian augmented scale. - public static let lydianAugmented = ScaleType(intervals: [.P1, .M2, .M3, .A4, .A5, .M6, .M7], description: "Lydian Augmented") - - /// Neopolitan major scale. - public static let neopolitanMajor = ScaleType(intervals: [.P1, .m2, .m3, .P4, .P5, .M6, .M7], description: "Neopolitan Major") - - /// Neopolitan minor scale. - public static let neopolitanMinor = ScaleType(intervals: [.P1, .m2, .m3, .P4, .P5, .m6, .m7], description: "Neopolitan Minor") - - /// Pelog scale. - public static let pelog = ScaleType(intervals: [.P1, .m2, .m3, .d5, .m7, .M7], description: "Pelog") - - /// Prometheus scale. - public static let prometheus = ScaleType(intervals: [.P1, .M2, .M3, .A4, .M6, .m7], description: "Prometheus") - - /// Prometheus neopolitan scale. - public static let prometheusNeopolitan = ScaleType(intervals: [.P1, .m2, .M3, .d5, .M6, .m7], description: "Prometheus Neopolitan") - - /// Six tone symmetrical scale. - public static let sixToneSymmetrical = ScaleType(intervals: [.P1, .m2, .M3, .P4, .m6, .M6], description: "Six Tone Symmetrical") - - /// Super locrian scale. - public static let superLocrian = ScaleType(intervals: [.P1, .m2, .m3, .M3, .d5, .m6, .m7], description: "Super Locrian") - - /// Lydian minor scale. - public static let lydianMinor = ScaleType(intervals: [.P1, .M2, .M3, .d5, .P5, .m6, .m7], description: "Lydian Minor") - - /// Lydian diminished scale. - public static let lydianDiminished = ScaleType(intervals: [.P1, .M2, .m3, .d5, .P5, .m6, .m7], description: "Lydian Diminished") - - /// Nine tone scale. - public static let nineTone = ScaleType(intervals: [.P1, .M2, .m3, .M3, .d5, .P5, .m6, .M6, .M7], description: "Nine Tone") - - /// Auxiliary diminished scale. - public static let auxiliaryDiminished = ScaleType(intervals: [.P1, .M2, .m3, .P4, .d5, .m6, .M6, .M7], description: "Auxiliary Diminished") - - /// Auxiliary augmaneted scale. - public static let auxiliaryAugmented = ScaleType(intervals: [.P1, .M2, .M3, .d5, .m6, .m7], description: "Auxiliary Augmented") - - /// Auxiliary diminished blues scale. - public static let auxiliaryDimBlues = ScaleType(intervals: [.P1, .m2, .m3, .M3, .d5, .P5, .M6, .m7], description: "Auxiliary Diminished Blues") - - /// Major locrian scale. - public static let majorLocrian = ScaleType(intervals: [.P1, .M2, .M3, .P4, .d5, .m6, .m7], description: "Major Locrian") - - /// Overtone scale. - public static let overtone = ScaleType(intervals: [.P1, .M2, .M3, .d5, .P5, .M6, .m7], description: "Overtone") - - /// Diminished whole tone scale. - public static let diminishedWholeTone = ScaleType(intervals: [.P1, .m2, .m3, .M3, .d5, .m6, .m7], description: "Diminished Whole Tone") - - /// Dominant seventh scale. - public static let dominant7th = ScaleType(intervals: [.P1, .M2, .M3, .P4, .P5, .M6, .m7], description: "Dominant 7th") - - /// Altered scale - public static let altered = ScaleType(intervals: [.P1, .m2, .m3, .M3, .d5, .m6, .m7], description: "Altered") - - /// Arabian scale - public static let arabian = ScaleType(intervals: [.P1, .M2, .M3, .P4, .d5, .m6, .m7], description: "Arabian") - - /// Ionian augmented scale - public static let ionianAugmented = ScaleType(intervals: [.P1, .M2, .M3, .P4, .m6, .M6, .M7], description: "Ionian Augmented") - - /// Balinese scale - public static let balinese = ScaleType(intervals: [.P1, .m2, .m3, .P5, .m6], description: "Balinese") - - /// Byzantine scale - public static let byzantine = ScaleType(intervals: [.P1, .m2, .M3, .P4, .P5, .m6, .M7], description: "Byzantine") - - /// Chinese scale - public static let chinese = ScaleType(intervals: [.P1, .M3, .d5, .P5, .M7], description: "Chinese") - - /// Dorian #4 scale - public static let dorianSharp4 = ScaleType(intervals: [.P1, .M2, .m3, .d5, .P5, .M6, .m7], description: "Dorian #4") - - /// Dorian b2 scale - public static let dorianFlat2 = ScaleType(intervals: [.P1, .m2, .m3, .P4, .P5, .M6, .m7], description: "Dorian b2") - - /// Hindu scale - public static let hindu = ScaleType(intervals: [.P1, .M2, .M3, .P4, .P5, .m6, .m7], description: "Hindu") - - /// Hirajoshi scale - public static let hirajoshi = ScaleType(intervals: [.P1, .M2, .m3, .P5, .m6], description: "Hirajoshi") - - /// Hungarian major scale - public static let hungarianMajor = ScaleType(intervals: [.P1, .m3, .M3, .d5, .P5, .M6, .m7], description: "Hungarian Major") - - /// Hungarian minor scale - public static let hungarianMinor = ScaleType(intervals: [.P1, .M2, .m3, .A4, .P5, .m6, .M7], description: "Hungarian Minor") - - /// Ichikosucho scale - public static let ichikosucho = ScaleType(intervals: [.P1, .M2, .M3, .P4, .d5, .P5, .M6, .M7], description: "Ichikosucho") - - /// Kumoi scale - public static let kumoi = ScaleType(intervals: [.P1, .M2, .m3, .P5, .M6], description: "Kumoi") - - /// Locrian 2 scale - public static let locrian2 = ScaleType(intervals: [.P1, .M2, .m3, .P4, .d5, .m6, .m7], description: "Locrian 2") - - /// Locrian 3 scale - public static let locrian3 = ScaleType(intervals: [.P1, .m2, .M3, .P4, .d5, .m6, .m7], description: "Locrian 3") - - /// Locrian 6 scale - public static let locrian6 = ScaleType(intervals: [.P1, .m2, .m3, .P4, .d5, .M6, .m7], description: "Locrian 6") - - /// Lydian #2 scale - public static let lydianSharp2 = ScaleType(intervals: [.P1, .m3, .M3, .d5, .P5, .M6, .M7], description: "Lydian #2") - - /// Lydian b7 scale - public static let lydianFlat7 = ScaleType(intervals: [.P1, .M2, .M3, .d5, .P5, .M6, .m7], description: "Lydian b7") - - /// Phrygian Major scale - public static let phrygianMajor = ScaleType(intervals: [.P1, .m2, .M3, .P4, .P5, .m6, .m7], description: "Phrygian Major") - - /// Mixolydian b6 scale - public static let mixolydianFlat6 = ScaleType(intervals: [.P1, .M2, .M3, .P4, .P5, .m6, .m7], description: "Mixolydian b6") - - /// Mohammedan scale - public static let mohammedan = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .m6, .M7], description: "Mohammedan") - - /// Mongolian scale - public static let mongolian = ScaleType(intervals: [.P1, .M2, .M3, .P5, .M6], description: "Mongolian") - - /// Natural minor scale - public static let naturalMinor = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .m6, .m7], description: "Natural Minor") - - /// Neopolitan scale - public static let neopolitan = ScaleType(intervals: [.P1, .m2, .m3, .P4, .P5, .m6, .M7], description: "Neopolitan") - - /// Persian scale - public static let persian = ScaleType(intervals: [.P1, .m2, .M3, .P4, .d5, .m6, .M7], description: "Persian") - - /// Purvi theta scale - public static let purviTheta = ScaleType(intervals: [.P1, .m2, .M3, .d5, .P5, .m6, .M7], description: "Purvi Theta") - - /// Todi theta scale - public static let todiTheta = ScaleType(intervals: [.P1, .m2, .m3, .d5, .P5, .m6, .M7], description: "Todi Theta") - - /// Major bebop scale - public static let majorBebop = ScaleType(intervals: [.P1, .M2, .M3, .P4, .P5, .m6, .M6, .M7], description: "Major Bebop") - - /// Minor bebop scale - public static let minorBebop = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .M6, .m7, .M7], description: "Minor Bebop") - - /// Bebop dominant scale - public static let bebopDominant = ScaleType(intervals: [.P1, .M2, .M3, .P4, .P5, .M6, .m7, .M7], description: "Bebop Dominant") - - /// Tritone scale - public static let tritone = ScaleType(intervals: [.P1, .m2, .M3, .d5, .P5, .m7], description: "Tritone") - - /// Insen scale - public static let insen = ScaleType(intervals: [.P1, .m2, .P4, .P5, .m7], description: "Insen") - - /// Istrian scale - public static let istrian = ScaleType(intervals: [.P1, .m2, .m3, .d4, .d5, .P5], description: "Istrian") - - /// Gypsy scale - public static let gypsy = ScaleType(intervals: [.P1, .M2, .m3, .A4, .P5, .m6, .m7], description: "Gypsy") - - /// Iwato scale - public static let iwato = ScaleType(intervals: [.P1, .m2, .P4, .d5, .m7], description: "Iwato") - - /// Pfluke scale - public static let pfluke = ScaleType(intervals: [.P1, .M2, .m3, .A4, .P5, .M6, .M7], description: "Pfluke") - - /// Ukrainian dorian scale - public static let ukrainianDorian = ScaleType(intervals: [.P1, .M2, .m3, .A4, .P5, .M6, .m7], description: "Ukrainian Dorian") - - /// Yo scale - public static let yo = ScaleType(intervals: [.P1, .m3, .P4, .P5, .m7], description: "Yo") - - /// Algerian scale - public static let algerian = ScaleType(intervals: [.P1, .M2, .m3, .A4, .P5, .m6, .M7], description: "Algerian") - - /// Flamenco scale - public static let flamenco = ScaleType(intervals: [.P1, .m2, .M3, .P4, .P5, .m6, .M7], description: "Flamenco") - - /// Hawaiian scale - public static let hawaiian = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .M6, .M7], description: "Hawaiian") - - /// Maqam scale - public static let maqam = ScaleType(intervals: [.P1, .m2, .M3, .P4, .P5, .m6, .M7], description: "Maqam") - - /// Oriental scale - public static let oriental = ScaleType(intervals: [.P1, .m2, .M3, .P4, .d5, .M6, .m7], description: "Oriental") - - /// Jazz melodic minor scale - public static let jazzMelodicMinor = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .M6, .M7], description: "Jazz Melodic Minor") - - /// Lydian augmented #6 scale - public static let lydianAugmentedSharp6 = ScaleType(intervals: [.P1, .M2, .M3, .d5, .m6, .m7, .M7], description: "Lydian Augmented #6") - - /// Lydian augmented #2 scale - public static let lydianAugmentedSharp2 = ScaleType(intervals: [.P1, .m3, .M3, .d5, .m6, .M6, .M7], description: "Lydian Augmented #2") - - /// Dorian b5 scale - public static let dorianFlat5 = ScaleType(intervals: [.P1, .M2, .m3, .P4, .d5, .M6, .m7], description: "Dorian b5") - - /// Phrygian b4 scale - public static let phrygianFlat4 = ScaleType(intervals: [.P1, .m2, .m3, .M3, .P5, .m6, .m7], description: "Phrygian b4") - - /// Lydian b3 scale - public static let lydianFlat3 = ScaleType(intervals: [.P1, .M2, .m3, .d5, .P5, .M6, .M7], description: "Lydian b3") - - /// Lydian b6 scale - public static let lydianFlat6 = ScaleType(intervals: [.P1, .M2, .M3, .d5, .P5, .m6, .m7], description: "Lydian b6") - - /// Lydian #6 scale - public static let lydianSharp6 = ScaleType(intervals: [.P1, .M2, .M3, .d5, .P5, .m7, .M7], description: "Lydian #6") - - /// Lydian #2 #6 scale - public static let lydianSharp2Sharp6 = ScaleType(intervals: [.P1, .m3, .M3, .d5, .P5, .m7, .M7], description: "Lydian #2 #6") - - /// Mixolydian b2 scale - public static let mixolydianFlat2 = ScaleType(intervals: [.P1, .m2, .M3, .P4, .P5, .M6, .m7], description: "Mixolydian b2") - - /// Mixolydian augmented scale - public static let mixolydianAugmented = ScaleType(intervals: [.P1, .M2, .M3, .P4, .m6, .M6, .m7], description: "Mixolydian Augmented") - - /// Locrian diminished scale - public static let locrianDiminished = ScaleType(intervals: [.P1, .m2, .m3, .P4, .d5, .m6, .M6], description: "Locrian Diminished") - - /// Locrian diminished bb3 scale - public static let locrianDiminishedFlatFlat3 = ScaleType(intervals: [.P1, .m2, .P4, .d5, .m6, .M6], description: "Locrian Diminished bb3") - - /// Ionian #2 scale - public static let ionianSharp2 = ScaleType(intervals: [.P1, .m3, .M3, .P4, .P5, .M6, .M7], description: "Ionian #2") - - /// Super locrian Diminished bb3 scale - public static let superLocrianDiminshedFlatFlat3 = ScaleType(intervals: [.P1, .m2, .M2, .M3, .d5, .m6, .M6], description: "Super Locrian Diminished bb3") - - /// Ultraphrygian scale - public static let ultraphrygian = ScaleType(intervals: [.P1, .m2, .m3, .M3, .P5, .m6, .M6], description: "Ultraphrygian") - - /// Ionian Augmented #2 scale - public static let ionianAugmentedSharp2 = ScaleType(intervals: [.P1, .m3, .M3, .P4, .m6, .M6, .M7], description: "Ionian Augmented #2") - - /// Major blues hexatonic scale - public static let majorBluesHexatonic = ScaleType(intervals: [.P1, .M2, .m3, .M3, .P5, .M6], description: "Major Blues Hexatonic") - - /// Minor blues hexatonic scale - public static let minorBluesHexatonic = ScaleType(intervals: [.P1, .m3, .P4, .d5, .P5, .m7], description: "Minor Blues Hexatonic") - - /// Man gong scale - public static let manGong = ScaleType(intervals: [.P1, .m3, .P4, .m6, .m7], description: "Man Gong") - - /// Ritsusen scale - public static let ritsusen = ScaleType(intervals: [.P1, .M2, .P4, .P5, .M6], description: "Ritsusen") - - /// An array of all `ScaleType` values. - public static var all: [ScaleType] { - return [ - .major, - .minor, - .harmonicMinor, - .melodicMinor, - .naturalMinor, - .ionian, - .ionianSharp2, - .ionianAugmented, - .ionianAugmentedSharp2, - .aeolian, - .dorian, - .dorianSharp4, - .dorianFlat2, - .dorianFlat5, - .mixolydian, - .mixolydianAugmented, - .mixolydianFlat2, - .mixolydianFlat6, - .phrygian, - .phrygianMajor, - .phrygianFlat4, - .ultraphrygian, - .lydian, - .lydianMinor, - .lydianDiminished, - .lydianSharp2, - .lydianSharp6, - .lydianSharp2Sharp6, - .lydianFlat3, - .lydianFlat6, - .lydianFlat7, - .lydianAugmented, - .lydianAugmentedSharp2, - .lydianAugmentedSharp6, - .locrian, - .locrian2, - .locrian3, - .locrian6, - .majorLocrian, - .locrianDiminished, - .locrianDiminishedFlatFlat3, - .superLocrian, - .superLocrianDiminshedFlatFlat3, - .chromatic, - .whole, - .altered, - .augmented, - .dominant7th, - .halfDiminished, - .wholeDiminished, - .leadingWholeTone, - .diminishedWholeTone, - .overtone, - .nineTone, - .diatonic, - .enigmatic, - .doubleHarmonic, - .auxiliaryDiminished, - .auxiliaryAugmented, - .auxiliaryDimBlues, - .sixToneSymmetrical, - .neopolitan, - .neopolitanMajor, - .neopolitanMinor, - .prometheus, - .prometheusNeopolitan, - .pelog, - .pentatonicMajor, - .pentatonicMinor, - .pentatonicBlues, - .pentatonicNeutral, - .majorBluesHexatonic, - .minorBluesHexatonic, - .jazzMelodicMinor, - .spanishGypsy, - .eightToneSpanish, - .hungarianMajor, - .hungarianMinor, - .romanianMinor, - .flamenco, - .gypsy, - .majorBebop, - .minorBebop, - .bebopDominant, - .chinese, - .oriental, - .hirajoshi, - .ichikosucho, - .kumoi, - .yo, - .iwato, - .mongolian, - .hindu, - .byzantine, - .arabian, - .persian, - .mohammedan, - .maqam, - .algerian, - .balinese, - .purviTheta, - .todiTheta, - .tritone, - .insen, - .istrian, - .pfluke, - .ukrainianDorian, - .hawaiian, - .manGong, - .ritsusen - ] - } + /// Major scale. + public static let major = ScaleType(intervals: ScaleType.ionian.intervals, description: "Major") + + /// Minor scale. + public static let minor = ScaleType(intervals: ScaleType.aeolian.intervals, description: "Minor") + + /// Harmonic minor scale. + public static let harmonicMinor = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .m6, .M7], description: "Harmonic Minor") + + /// Melodic minor scale. + public static let melodicMinor = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .M6, .M7], description: "Melodic Minor") + + /// Pentatonic major scale. + public static let pentatonicMajor = ScaleType(intervals: [.P1, .M2, .M3, .P5, .M6], description: "Pentatonic Major") + + /// Pentatonic minor scale. + public static let pentatonicMinor = ScaleType(intervals: [.P1, .m3, .P4, .P5, .m7], description: "Pentatonic Minor") + + /// Pentatonic blues scale. + public static let pentatonicBlues = ScaleType(intervals: [.P1, .m3, .P4, .d5, .P5, .m7], description: "Pentatonic Blues") + + /// Pentatonic neutral scale. + public static let pentatonicNeutral = ScaleType(intervals: [.P1, .M2, .P4, .P5, .m7], description: "Pentatonic Neutral") + + /// Ionian scale. + public static let ionian = ScaleType(intervals: [.P1, .M2, .M3, .P4, .P5, .M6, .M7], description: "Ionian") + + /// Aeolian scale. + public static let aeolian = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .m6, .m7], description: "Aeolian") + + /// Dorian scale. + public static let dorian = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .M6, .m7], description: "Dorian") + + /// Mixolydian scale. + public static let mixolydian = ScaleType(intervals: [.P1, .M2, .M3, .P4, .P5, .M6, .m7], description: "Mixolydian") + + /// Phrygian scale. + public static let phrygian = ScaleType(intervals: [.P1, .m2, .m3, .P4, .P5, .m6, .m7], description: "Phrygian") + + /// Lydian scale. + public static let lydian = ScaleType(intervals: [.P1, .M2, .M3, .A4, .P5, .M6, .M7], description: "Lydian") + + /// Locrian scale. + public static let locrian = ScaleType(intervals: [.P1, .m2, .m3, .P4, .d5, .m6, .m7], description: "Locrian") + + /// Half diminished scale. + public static let halfDiminished = ScaleType(intervals: [.P1, .m2, .m3, .M3, .d5, .P5, .M6, .m7], description: "Half Diminished") + + /// Whole diminished scale. + public static let wholeDiminished = ScaleType(intervals: [.P1, .M2, .m3, .P4, .d5, .m6, .M6, .M7], description: "Whole Diminished") + + /// Whole scale. + public static let whole = ScaleType(intervals: [.P1, .M2, .M3, .d5, .m6, .m7], description: "Whole") + + /// Augmented scale. + public static let augmented = ScaleType(intervals: [.m3, .M3, .P5, .m6, .M7], description: "Augmented") + + /// Chromatic scale. + public static let chromatic = ScaleType(intervals: [.P1, .m2, .M2, .m3, .M3, .P4, .d5, .P5, .m6, .M6, .m7, .M7], description: "Chromatic") + + /// Roumanian minor scale. + public static let romanianMinor = ScaleType(intervals: [.P1, .M2, .m3, .d5, .P5, .M6, .m7], description: "Romanian Minor") + + /// Spanish gypsy scale. + public static let spanishGypsy = ScaleType(intervals: [.P1, .m2, .M3, .P4, .P5, .m6, .m7], description: "Spanish Gypsy") + + /// Diatonic scale. + public static let diatonic = ScaleType(intervals: [.P1, .M2, .M3, .P5, .M6], description: "Diatonic") + + /// Dobule harmonic scale. + public static let doubleHarmonic = ScaleType(intervals: [.P1, .m2, .M3, .P4, .P5, .m6, .M7], description: "Double Harmonic") + + /// Eight tone spanish scale. + public static let eightToneSpanish = ScaleType(intervals: [.P1, .m2, .m3, .M3, .P4, .d5, .m6, .m7], description: "Eight Tone Spanish") + + /// Enigmatic scale. + public static let enigmatic = ScaleType(intervals: [.P1, .m2, .M3, .A4, .A5, .A6, .M7], description: "Enigmatic") + + /// Leading whole tone scale. + public static let leadingWholeTone = ScaleType(intervals: [.P1, .M2, .M3, .d5, .m6, .M6, .m7], description: "Leading Whole Tone") + + /// Lydian augmented scale. + public static let lydianAugmented = ScaleType(intervals: [.P1, .M2, .M3, .A4, .A5, .M6, .M7], description: "Lydian Augmented") + + /// Neopolitan major scale. + public static let neopolitanMajor = ScaleType(intervals: [.P1, .m2, .m3, .P4, .P5, .M6, .M7], description: "Neopolitan Major") + + /// Neopolitan minor scale. + public static let neopolitanMinor = ScaleType(intervals: [.P1, .m2, .m3, .P4, .P5, .m6, .m7], description: "Neopolitan Minor") + + /// Pelog scale. + public static let pelog = ScaleType(intervals: [.P1, .m2, .m3, .d5, .m7, .M7], description: "Pelog") + + /// Prometheus scale. + public static let prometheus = ScaleType(intervals: [.P1, .M2, .M3, .A4, .M6, .m7], description: "Prometheus") + + /// Prometheus neopolitan scale. + public static let prometheusNeopolitan = ScaleType(intervals: [.P1, .m2, .M3, .d5, .M6, .m7], description: "Prometheus Neopolitan") + + /// Six tone symmetrical scale. + public static let sixToneSymmetrical = ScaleType(intervals: [.P1, .m2, .M3, .P4, .m6, .M6], description: "Six Tone Symmetrical") + + /// Super locrian scale. + public static let superLocrian = ScaleType(intervals: [.P1, .m2, .m3, .M3, .d5, .m6, .m7], description: "Super Locrian") + + /// Lydian minor scale. + public static let lydianMinor = ScaleType(intervals: [.P1, .M2, .M3, .d5, .P5, .m6, .m7], description: "Lydian Minor") + + /// Lydian diminished scale. + public static let lydianDiminished = ScaleType(intervals: [.P1, .M2, .m3, .d5, .P5, .m6, .m7], description: "Lydian Diminished") + + /// Nine tone scale. + public static let nineTone = ScaleType(intervals: [.P1, .M2, .m3, .M3, .d5, .P5, .m6, .M6, .M7], description: "Nine Tone") + + /// Auxiliary diminished scale. + public static let auxiliaryDiminished = ScaleType(intervals: [.P1, .M2, .m3, .P4, .d5, .m6, .M6, .M7], description: "Auxiliary Diminished") + + /// Auxiliary augmaneted scale. + public static let auxiliaryAugmented = ScaleType(intervals: [.P1, .M2, .M3, .d5, .m6, .m7], description: "Auxiliary Augmented") + + /// Auxiliary diminished blues scale. + public static let auxiliaryDimBlues = ScaleType(intervals: [.P1, .m2, .m3, .M3, .d5, .P5, .M6, .m7], description: "Auxiliary Diminished Blues") + + /// Major locrian scale. + public static let majorLocrian = ScaleType(intervals: [.P1, .M2, .M3, .P4, .d5, .m6, .m7], description: "Major Locrian") + + /// Overtone scale. + public static let overtone = ScaleType(intervals: [.P1, .M2, .M3, .d5, .P5, .M6, .m7], description: "Overtone") + + /// Diminished whole tone scale. + public static let diminishedWholeTone = ScaleType(intervals: [.P1, .m2, .m3, .M3, .d5, .m6, .m7], description: "Diminished Whole Tone") + + /// Dominant seventh scale. + public static let dominant7th = ScaleType(intervals: [.P1, .M2, .M3, .P4, .P5, .M6, .m7], description: "Dominant 7th") + + /// Altered scale + public static let altered = ScaleType(intervals: [.P1, .m2, .m3, .M3, .d5, .m6, .m7], description: "Altered") + + /// Arabian scale + public static let arabian = ScaleType(intervals: [.P1, .M2, .M3, .P4, .d5, .m6, .m7], description: "Arabian") + + /// Ionian augmented scale + public static let ionianAugmented = ScaleType(intervals: [.P1, .M2, .M3, .P4, .m6, .M6, .M7], description: "Ionian Augmented") + + /// Balinese scale + public static let balinese = ScaleType(intervals: [.P1, .m2, .m3, .P5, .m6], description: "Balinese") + + /// Byzantine scale + public static let byzantine = ScaleType(intervals: [.P1, .m2, .M3, .P4, .P5, .m6, .M7], description: "Byzantine") + + /// Chinese scale + public static let chinese = ScaleType(intervals: [.P1, .M3, .d5, .P5, .M7], description: "Chinese") + + /// Dorian #4 scale + public static let dorianSharp4 = ScaleType(intervals: [.P1, .M2, .m3, .d5, .P5, .M6, .m7], description: "Dorian #4") + + /// Dorian b2 scale + public static let dorianFlat2 = ScaleType(intervals: [.P1, .m2, .m3, .P4, .P5, .M6, .m7], description: "Dorian b2") + + /// Hindu scale + public static let hindu = ScaleType(intervals: [.P1, .M2, .M3, .P4, .P5, .m6, .m7], description: "Hindu") + + /// Hirajoshi scale + public static let hirajoshi = ScaleType(intervals: [.P1, .M2, .m3, .P5, .m6], description: "Hirajoshi") + + /// Hungarian major scale + public static let hungarianMajor = ScaleType(intervals: [.P1, .m3, .M3, .d5, .P5, .M6, .m7], description: "Hungarian Major") + + /// Hungarian minor scale + public static let hungarianMinor = ScaleType(intervals: [.P1, .M2, .m3, .A4, .P5, .m6, .M7], description: "Hungarian Minor") + + /// Ichikosucho scale + public static let ichikosucho = ScaleType(intervals: [.P1, .M2, .M3, .P4, .d5, .P5, .M6, .M7], description: "Ichikosucho") + + /// Kumoi scale + public static let kumoi = ScaleType(intervals: [.P1, .M2, .m3, .P5, .M6], description: "Kumoi") + + /// Locrian 2 scale + public static let locrian2 = ScaleType(intervals: [.P1, .M2, .m3, .P4, .d5, .m6, .m7], description: "Locrian 2") + + /// Locrian 3 scale + public static let locrian3 = ScaleType(intervals: [.P1, .m2, .M3, .P4, .d5, .m6, .m7], description: "Locrian 3") + + /// Locrian 6 scale + public static let locrian6 = ScaleType(intervals: [.P1, .m2, .m3, .P4, .d5, .M6, .m7], description: "Locrian 6") + + /// Lydian #2 scale + public static let lydianSharp2 = ScaleType(intervals: [.P1, .m3, .M3, .d5, .P5, .M6, .M7], description: "Lydian #2") + + /// Lydian b7 scale + public static let lydianFlat7 = ScaleType(intervals: [.P1, .M2, .M3, .d5, .P5, .M6, .m7], description: "Lydian b7") + + /// Phrygian Major scale + public static let phrygianMajor = ScaleType(intervals: [.P1, .m2, .M3, .P4, .P5, .m6, .m7], description: "Phrygian Major") + + /// Mixolydian b6 scale + public static let mixolydianFlat6 = ScaleType(intervals: [.P1, .M2, .M3, .P4, .P5, .m6, .m7], description: "Mixolydian b6") + + /// Mohammedan scale + public static let mohammedan = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .m6, .M7], description: "Mohammedan") + + /// Mongolian scale + public static let mongolian = ScaleType(intervals: [.P1, .M2, .M3, .P5, .M6], description: "Mongolian") + + /// Natural minor scale + public static let naturalMinor = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .m6, .m7], description: "Natural Minor") + + /// Neopolitan scale + public static let neopolitan = ScaleType(intervals: [.P1, .m2, .m3, .P4, .P5, .m6, .M7], description: "Neopolitan") + + /// Persian scale + public static let persian = ScaleType(intervals: [.P1, .m2, .M3, .P4, .d5, .m6, .M7], description: "Persian") + + /// Purvi theta scale + public static let purviTheta = ScaleType(intervals: [.P1, .m2, .M3, .d5, .P5, .m6, .M7], description: "Purvi Theta") + + /// Todi theta scale + public static let todiTheta = ScaleType(intervals: [.P1, .m2, .m3, .d5, .P5, .m6, .M7], description: "Todi Theta") + + /// Major bebop scale + public static let majorBebop = ScaleType(intervals: [.P1, .M2, .M3, .P4, .P5, .m6, .M6, .M7], description: "Major Bebop") + + /// Minor bebop scale + public static let minorBebop = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .M6, .m7, .M7], description: "Minor Bebop") + + /// Bebop dominant scale + public static let bebopDominant = ScaleType(intervals: [.P1, .M2, .M3, .P4, .P5, .M6, .m7, .M7], description: "Bebop Dominant") + + /// Tritone scale + public static let tritone = ScaleType(intervals: [.P1, .m2, .M3, .d5, .P5, .m7], description: "Tritone") + + /// Insen scale + public static let insen = ScaleType(intervals: [.P1, .m2, .P4, .P5, .m7], description: "Insen") + + /// Istrian scale + public static let istrian = ScaleType(intervals: [.P1, .m2, .m3, .d4, .d5, .P5], description: "Istrian") + + /// Gypsy scale + public static let gypsy = ScaleType(intervals: [.P1, .M2, .m3, .A4, .P5, .m6, .m7], description: "Gypsy") + + /// Iwato scale + public static let iwato = ScaleType(intervals: [.P1, .m2, .P4, .d5, .m7], description: "Iwato") + + /// Pfluke scale + public static let pfluke = ScaleType(intervals: [.P1, .M2, .m3, .A4, .P5, .M6, .M7], description: "Pfluke") + + /// Ukrainian dorian scale + public static let ukrainianDorian = ScaleType(intervals: [.P1, .M2, .m3, .A4, .P5, .M6, .m7], description: "Ukrainian Dorian") + + /// Yo scale + public static let yo = ScaleType(intervals: [.P1, .m3, .P4, .P5, .m7], description: "Yo") + + /// Algerian scale + public static let algerian = ScaleType(intervals: [.P1, .M2, .m3, .A4, .P5, .m6, .M7], description: "Algerian") + + /// Flamenco scale + public static let flamenco = ScaleType(intervals: [.P1, .m2, .M3, .P4, .P5, .m6, .M7], description: "Flamenco") + + /// Hawaiian scale + public static let hawaiian = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .M6, .M7], description: "Hawaiian") + + /// Maqam scale + public static let maqam = ScaleType(intervals: [.P1, .m2, .M3, .P4, .P5, .m6, .M7], description: "Maqam") + + /// Oriental scale + public static let oriental = ScaleType(intervals: [.P1, .m2, .M3, .P4, .d5, .M6, .m7], description: "Oriental") + + /// Jazz melodic minor scale + public static let jazzMelodicMinor = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .M6, .M7], description: "Jazz Melodic Minor") + + /// Lydian augmented #6 scale + public static let lydianAugmentedSharp6 = ScaleType(intervals: [.P1, .M2, .M3, .d5, .m6, .m7, .M7], description: "Lydian Augmented #6") + + /// Lydian augmented #2 scale + public static let lydianAugmentedSharp2 = ScaleType(intervals: [.P1, .m3, .M3, .d5, .m6, .M6, .M7], description: "Lydian Augmented #2") + + /// Dorian b5 scale + public static let dorianFlat5 = ScaleType(intervals: [.P1, .M2, .m3, .P4, .d5, .M6, .m7], description: "Dorian b5") + + /// Phrygian b4 scale + public static let phrygianFlat4 = ScaleType(intervals: [.P1, .m2, .m3, .M3, .P5, .m6, .m7], description: "Phrygian b4") + + /// Lydian b3 scale + public static let lydianFlat3 = ScaleType(intervals: [.P1, .M2, .m3, .d5, .P5, .M6, .M7], description: "Lydian b3") + + /// Lydian b6 scale + public static let lydianFlat6 = ScaleType(intervals: [.P1, .M2, .M3, .d5, .P5, .m6, .m7], description: "Lydian b6") + + /// Lydian #6 scale + public static let lydianSharp6 = ScaleType(intervals: [.P1, .M2, .M3, .d5, .P5, .m7, .M7], description: "Lydian #6") + + /// Lydian #2 #6 scale + public static let lydianSharp2Sharp6 = ScaleType(intervals: [.P1, .m3, .M3, .d5, .P5, .m7, .M7], description: "Lydian #2 #6") + + /// Mixolydian b2 scale + public static let mixolydianFlat2 = ScaleType(intervals: [.P1, .m2, .M3, .P4, .P5, .M6, .m7], description: "Mixolydian b2") + + /// Mixolydian augmented scale + public static let mixolydianAugmented = ScaleType(intervals: [.P1, .M2, .M3, .P4, .m6, .M6, .m7], description: "Mixolydian Augmented") + + /// Locrian diminished scale + public static let locrianDiminished = ScaleType(intervals: [.P1, .m2, .m3, .P4, .d5, .m6, .M6], description: "Locrian Diminished") + + /// Locrian diminished bb3 scale + public static let locrianDiminishedFlatFlat3 = ScaleType(intervals: [.P1, .m2, .P4, .d5, .m6, .M6], description: "Locrian Diminished bb3") + + /// Ionian #2 scale + public static let ionianSharp2 = ScaleType(intervals: [.P1, .m3, .M3, .P4, .P5, .M6, .M7], description: "Ionian #2") + + /// Super locrian Diminished bb3 scale + public static let superLocrianDiminshedFlatFlat3 = ScaleType(intervals: [.P1, .m2, .M2, .M3, .d5, .m6, .M6], description: "Super Locrian Diminished bb3") + + /// Ultraphrygian scale + public static let ultraphrygian = ScaleType(intervals: [.P1, .m2, .m3, .M3, .P5, .m6, .M6], description: "Ultraphrygian") + + /// Ionian Augmented #2 scale + public static let ionianAugmentedSharp2 = ScaleType(intervals: [.P1, .m3, .M3, .P4, .m6, .M6, .M7], description: "Ionian Augmented #2") + + /// Major blues hexatonic scale + public static let majorBluesHexatonic = ScaleType(intervals: [.P1, .M2, .m3, .M3, .P5, .M6], description: "Major Blues Hexatonic") + + /// Minor blues hexatonic scale + public static let minorBluesHexatonic = ScaleType(intervals: [.P1, .m3, .P4, .d5, .P5, .m7], description: "Minor Blues Hexatonic") + + /// Man gong scale + public static let manGong = ScaleType(intervals: [.P1, .m3, .P4, .m6, .m7], description: "Man Gong") + + /// Ritsusen scale + public static let ritsusen = ScaleType(intervals: [.P1, .M2, .P4, .P5, .M6], description: "Ritsusen") + + /// An array of all `ScaleType` values. + public static var all: [ScaleType] { + return [ + .major, + .minor, + .harmonicMinor, + .melodicMinor, + .naturalMinor, + .ionian, + .ionianSharp2, + .ionianAugmented, + .ionianAugmentedSharp2, + .aeolian, + .dorian, + .dorianSharp4, + .dorianFlat2, + .dorianFlat5, + .mixolydian, + .mixolydianAugmented, + .mixolydianFlat2, + .mixolydianFlat6, + .phrygian, + .phrygianMajor, + .phrygianFlat4, + .ultraphrygian, + .lydian, + .lydianMinor, + .lydianDiminished, + .lydianSharp2, + .lydianSharp6, + .lydianSharp2Sharp6, + .lydianFlat3, + .lydianFlat6, + .lydianFlat7, + .lydianAugmented, + .lydianAugmentedSharp2, + .lydianAugmentedSharp6, + .locrian, + .locrian2, + .locrian3, + .locrian6, + .majorLocrian, + .locrianDiminished, + .locrianDiminishedFlatFlat3, + .superLocrian, + .superLocrianDiminshedFlatFlat3, + .chromatic, + .whole, + .altered, + .augmented, + .dominant7th, + .halfDiminished, + .wholeDiminished, + .leadingWholeTone, + .diminishedWholeTone, + .overtone, + .nineTone, + .diatonic, + .enigmatic, + .doubleHarmonic, + .auxiliaryDiminished, + .auxiliaryAugmented, + .auxiliaryDimBlues, + .sixToneSymmetrical, + .neopolitan, + .neopolitanMajor, + .neopolitanMinor, + .prometheus, + .prometheusNeopolitan, + .pelog, + .pentatonicMajor, + .pentatonicMinor, + .pentatonicBlues, + .pentatonicNeutral, + .majorBluesHexatonic, + .minorBluesHexatonic, + .jazzMelodicMinor, + .spanishGypsy, + .eightToneSpanish, + .hungarianMajor, + .hungarianMinor, + .romanianMinor, + .flamenco, + .gypsy, + .majorBebop, + .minorBebop, + .bebopDominant, + .chinese, + .oriental, + .hirajoshi, + .ichikosucho, + .kumoi, + .yo, + .iwato, + .mongolian, + .hindu, + .byzantine, + .arabian, + .persian, + .mohammedan, + .maqam, + .algerian, + .balinese, + .purviTheta, + .todiTheta, + .tritone, + .insen, + .istrian, + .pfluke, + .ukrainianDorian, + .hawaiian, + .manGong, + .ritsusen + ] + } } diff --git a/Sources/MusicTheory/Tempo.swift b/Sources/MusicTheory/Tempo.swift index f365468..d3c5c3d 100644 --- a/Sources/MusicTheory/Tempo.swift +++ b/Sources/MusicTheory/Tempo.swift @@ -12,57 +12,57 @@ import Foundation /// Defines the tempo of the music with beats per second and time signature. public struct Tempo: Codable, Hashable, CustomStringConvertible { - /// Time signature of music. - public var timeSignature: TimeSignature - /// Beats per minutes. - public var bpm: Double - - /// Initilizes tempo with time signature and BPM. - /// - /// - Parameters: - /// - timeSignature: Time Signature. - /// - bpm: Beats per minute. - public init(timeSignature: TimeSignature = TimeSignature(), bpm: Double = 120.0) { - self.timeSignature = timeSignature - self.bpm = bpm - } - - /// Caluclates the duration of a note value in seconds. - public func duration(of noteValue: NoteValue) -> TimeInterval { - let secondsPerBeat = 60.0 / bpm - return secondsPerBeat * (timeSignature.noteValue.beats / noteValue.type.beats) * noteValue.modifier.rawValue - } - - /// Calculates the note length in samples. Useful for sequencing notes sample accurate in the DSP. - /// - /// - Parameters: - /// - noteValue: Rate of the note you want to calculate sample length. - /// - sampleRate: Number of samples in a second. Defaults to 44100. - /// - Returns: Returns the sample length of a note value. - public func sampleLength(of noteValue: NoteValue, sampleRate: Double = 44100.0) -> Double { - let secondsPerBeat = 60.0 / bpm - return secondsPerBeat * sampleRate * ((Double(timeSignature.beats) * noteValue.type.beats) * noteValue.modifier.rawValue) - } - - /// Calculates the LFO speed of a note vaule in hertz. - public func hertz(of noteValue: NoteValue) -> Double { - return 1 / duration(of: noteValue) - } - - // MARK: Equatable - - /// Compares two Tempo instances and returns if they are identical. - /// - Parameters: - /// - lhs: Left hand side of the equation. - /// - rhs: Right hand side of the equation. - /// - Returns: Returns true if two instances are identical. - public static func == (lhs: Tempo, rhs: Tempo) -> Bool { - return lhs.hashValue == rhs.hashValue - } - - // MARK: CustomStringConvertible - - public var description: String { - return "\(bpm)" - } + /// Time signature of music. + public var timeSignature: TimeSignature + /// Beats per minutes. + public var bpm: Double + + /// Initilizes tempo with time signature and BPM. + /// + /// - Parameters: + /// - timeSignature: Time Signature. + /// - bpm: Beats per minute. + public init(timeSignature: TimeSignature = TimeSignature(), bpm: Double = 120.0) { + self.timeSignature = timeSignature + self.bpm = bpm + } + + /// Caluclates the duration of a note value in seconds. + public func duration(of noteValue: NoteValue) -> TimeInterval { + let secondsPerBeat = 60.0 / bpm + return secondsPerBeat * (timeSignature.noteValue.beats / noteValue.type.beats) * noteValue.modifier.rawValue + } + + /// Calculates the note length in samples. Useful for sequencing notes sample accurate in the DSP. + /// + /// - Parameters: + /// - noteValue: Rate of the note you want to calculate sample length. + /// - sampleRate: Number of samples in a second. Defaults to 44100. + /// - Returns: Returns the sample length of a note value. + public func sampleLength(of noteValue: NoteValue, sampleRate: Double = 44100.0) -> Double { + let secondsPerBeat = 60.0 / bpm + return secondsPerBeat * sampleRate * ((Double(timeSignature.beats) * noteValue.type.beats) * noteValue.modifier.rawValue) + } + + /// Calculates the LFO speed of a note vaule in hertz. + public func hertz(of noteValue: NoteValue) -> Double { + return 1 / duration(of: noteValue) + } + + // MARK: Equatable + + /// Compares two Tempo instances and returns if they are identical. + /// - Parameters: + /// - lhs: Left hand side of the equation. + /// - rhs: Right hand side of the equation. + /// - Returns: Returns true if two instances are identical. + public static func == (lhs: Tempo, rhs: Tempo) -> Bool { + return lhs.hashValue == rhs.hashValue + } + + // MARK: CustomStringConvertible + + public var description: String { + return "\(bpm)" + } } diff --git a/Sources/MusicTheory/TimeSignature.swift b/Sources/MusicTheory/TimeSignature.swift index 4574623..add48bc 100644 --- a/Sources/MusicTheory/TimeSignature.swift +++ b/Sources/MusicTheory/TimeSignature.swift @@ -12,24 +12,24 @@ import Foundation /// Defines how many beats in a measure with which note value. public struct TimeSignature: Codable, Hashable, CustomStringConvertible { - /// Beats per measure. - public var beats: Int - /// Note value per beat. - public var noteValue: NoteValueType - - /// Initilizes the time signature with beats per measure and the value of the notes in beat. - /// - /// - Parameters: - /// - beats: Number of beats in a measure - /// - noteValue: Note value of the beats. - public init(beats: Int = 4, noteValue: NoteValueType = .quarter) { - self.beats = beats - self.noteValue = noteValue - } - - // MARK: CustomStringConvertible - - public var description: String { - return "\(beats)/\(Int(noteValue.beats))" - } + /// Beats per measure. + public var beats: Int + /// Note value per beat. + public var noteValue: NoteValueType + + /// Initilizes the time signature with beats per measure and the value of the notes in beat. + /// + /// - Parameters: + /// - beats: Number of beats in a measure + /// - noteValue: Note value of the beats. + public init(beats: Int = 4, noteValue: NoteValueType = .quarter) { + self.beats = beats + self.noteValue = noteValue + } + + // MARK: CustomStringConvertible + + public var description: String { + return "\(beats)/\(Int(noteValue.beats))" + } } diff --git a/Tests/MusicTheoryTests/MusicTheoryTests.swift b/Tests/MusicTheoryTests/MusicTheoryTests.swift index 4372eb4..67e920c 100644 --- a/Tests/MusicTheoryTests/MusicTheoryTests.swift +++ b/Tests/MusicTheoryTests/MusicTheoryTests.swift @@ -10,381 +10,381 @@ import XCTest class MusicTheoryTests: XCTestCase { - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } } // MARK: - Note Tests extension MusicTheoryTests { - func testIntervals() { - let key = Key(type: .c) - let pitch = Pitch(key: key, octave: 1) - XCTAssert((pitch + 12).octave == pitch.octave + 1) - XCTAssert((pitch + 1).key == Key(type: .c, accidental: .sharp)) - XCTAssert((pitch - 1) == Pitch(key: Key(type: .b), octave: 0)) - - let c1 = Pitch(key: Key(type: .c), octave: 1) - let d1 = Pitch(key: Key(type: .d), octave: 1) - XCTAssert(d1 - c1 == .M2) - } - - func testAccidentals() { - XCTAssert(Accidental.flat * 2 == Accidental.doubleFlat) - XCTAssert(Accidental.doubleFlat / 2 == Accidental.flat) - XCTAssert(Accidental.sharps(amount: 2) - 2 == Accidental.natural) - XCTAssert(Accidental.flats(amount: 2) + 2 == 0) - XCTAssert(Accidental.sharps(amount: 2) + Accidental.sharps(amount: 1) == Accidental.sharps(amount: 3)) - XCTAssert(Accidental(integerLiteral: -3) + Accidental(rawValue: 3)! == 0) - } - - func testKeys() { - let d = Key.KeyType.d - XCTAssert(d.key(at: -2) == .b) - XCTAssert(d.key(at: -19) == .f) - XCTAssert(d.key(at: 12) == .b) - XCTAssert(d.key(at: 0) == .d) - XCTAssert(d.key(at: 1) == .e) - XCTAssert(d.key(at: 2) == .f) - XCTAssert(d.key(at: -3) == .a) - XCTAssert(d.key(at: -301) == .d) - - let f = Key.KeyType.f - XCTAssert(f.key(at: -3) == .c) - - let k: Key = "a##b" - XCTAssert(k.accidental == .sharp && k.type == .a) - - let b = Key(type: .b, accidental: .natural) - XCTAssert(Key(type: .c, accidental: .flat) == b) - XCTAssert(Key(type: .c, accidental: .sharps(amount: 23)) == b) - XCTAssert(Key(type: .c, accidental: .flats(amount: 13)) == b) - XCTAssert(Key(type: .c, accidental: .flats(amount: 25)) == b) - XCTAssert(Key(type: .c, accidental: .flats(amount: 24)) != b) - } - - func testPitches() { - let c0: Pitch = 12 - XCTAssert(c0.octave == 0 && c0.key.accidental == .natural && c0.key.type == .c) - XCTAssert(c0 - 12 == 0) - - var pitch = Pitch(midiNote: 127) - XCTAssert(pitch.key == Key(type: .g)) - pitch = Pitch(midiNote: 0) - XCTAssert(pitch.key == Key(type: .c)) - pitch = Pitch(midiNote: 66, preferSharps: false) - XCTAssert(pitch.key == Key(type: .g, accidental: .flat)) - - let c1 = Pitch(key: Key(type: .c), octave: 1) - XCTAssert(c1 + .m2 == Pitch(key: Key(type: .d, accidental: .flat), octave: 1)) - XCTAssert(c1 + .M2 == Pitch(key: Key(type: .d, accidental: .natural), octave: 1)) - XCTAssert(c1 + .m3 == Pitch(key: Key(type: .e, accidental: .flat), octave: 1)) - XCTAssert(c1 + .M3 == Pitch(key: Key(type: .e, accidental: .natural), octave: 1)) - XCTAssert(c1 + .P8 == Pitch(key: Key(type: .c, accidental: .natural), octave: 2)) - - let d1 = Pitch(key: Key(type: .d), octave: 1) - XCTAssert(d1 - .m2 == Pitch(key: Key(type: .c, accidental: .sharp), octave: 1)) - XCTAssert(d1 - .M2 == Pitch(key: Key(type: .c, accidental: .natural), octave: 1)) - - let p: Pitch = "f#-5" - XCTAssert(p.key === Key(type: .f, accidental: .sharp)) - XCTAssert(p.octave == -5) + func testIntervals() { + let key = Key(type: .c) + let pitch = Pitch(key: key, octave: 1) + XCTAssert((pitch + 12).octave == pitch.octave + 1) + XCTAssert((pitch + 1).key == Key(type: .c, accidental: .sharp)) + XCTAssert((pitch - 1) == Pitch(key: Key(type: .b), octave: 0)) + + let c1 = Pitch(key: Key(type: .c), octave: 1) + let d1 = Pitch(key: Key(type: .d), octave: 1) + XCTAssert(d1 - c1 == .M2) + } - let uppercasePitch: Pitch = "A#3" - XCTAssert(uppercasePitch.key === Key(type: .a, accidental: .sharp)) - XCTAssert(uppercasePitch.octave == 3) + func testAccidentals() { + XCTAssert(Accidental.flat * 2 == Accidental.doubleFlat) + XCTAssert(Accidental.doubleFlat / 2 == Accidental.flat) + XCTAssert(Accidental.sharps(amount: 2) - 2 == Accidental.natural) + XCTAssert(Accidental.flats(amount: 2) + 2 == 0) + XCTAssert(Accidental.sharps(amount: 2) + Accidental.sharps(amount: 1) == Accidental.sharps(amount: 3)) + XCTAssert(Accidental(integerLiteral: -3) + Accidental(rawValue: 3)! == 0) + } - let uppercasePitch2: Pitch = "F4" - XCTAssert(uppercasePitch2.key === Key(type: .f, accidental: .natural)) - XCTAssert(uppercasePitch2.octave == 4) - } - - func testFrequency() { - let note = Pitch(key: Key(type: .a), octave: 4) - XCTAssertEqual(note.frequency, 440.0) - - let a4 = Pitch.nearest(frequency: 440.0) - XCTAssertEqual(note, a4) - } + func testKeys() { + let d = Key.KeyType.d + XCTAssert(d.key(at: -2) == .b) + XCTAssert(d.key(at: -19) == .f) + XCTAssert(d.key(at: 12) == .b) + XCTAssert(d.key(at: 0) == .d) + XCTAssert(d.key(at: 1) == .e) + XCTAssert(d.key(at: 2) == .f) + XCTAssert(d.key(at: -3) == .a) + XCTAssert(d.key(at: -301) == .d) + + let f = Key.KeyType.f + XCTAssert(f.key(at: -3) == .c) + + let k: Key = "a##b" + XCTAssert(k.accidental == .sharp && k.type == .a) + + let b = Key(type: .b, accidental: .natural) + XCTAssert(Key(type: .c, accidental: .flat) == b) + XCTAssert(Key(type: .c, accidental: .sharps(amount: 23)) == b) + XCTAssert(Key(type: .c, accidental: .flats(amount: 13)) == b) + XCTAssert(Key(type: .c, accidental: .flats(amount: 25)) == b) + XCTAssert(Key(type: .c, accidental: .flats(amount: 24)) != b) + } + + func testPitches() { + let c0: Pitch = 12 + XCTAssert(c0.octave == 0 && c0.key.accidental == .natural && c0.key.type == .c) + XCTAssert(c0 - 12 == 0) + + var pitch = Pitch(midiNote: 127) + XCTAssert(pitch.key == Key(type: .g)) + pitch = Pitch(midiNote: 0) + XCTAssert(pitch.key == Key(type: .c)) + pitch = Pitch(midiNote: 66, preferSharps: false) + XCTAssert(pitch.key == Key(type: .g, accidental: .flat)) + + let c1 = Pitch(key: Key(type: .c), octave: 1) + XCTAssert(c1 + .m2 == Pitch(key: Key(type: .d, accidental: .flat), octave: 1)) + XCTAssert(c1 + .M2 == Pitch(key: Key(type: .d, accidental: .natural), octave: 1)) + XCTAssert(c1 + .m3 == Pitch(key: Key(type: .e, accidental: .flat), octave: 1)) + XCTAssert(c1 + .M3 == Pitch(key: Key(type: .e, accidental: .natural), octave: 1)) + XCTAssert(c1 + .P8 == Pitch(key: Key(type: .c, accidental: .natural), octave: 2)) + + let d1 = Pitch(key: Key(type: .d), octave: 1) + XCTAssert(d1 - .m2 == Pitch(key: Key(type: .c, accidental: .sharp), octave: 1)) + XCTAssert(d1 - .M2 == Pitch(key: Key(type: .c, accidental: .natural), octave: 1)) + + let p: Pitch = "f#-5" + XCTAssert(p.key === Key(type: .f, accidental: .sharp)) + XCTAssert(p.octave == -5) + + let uppercasePitch: Pitch = "A#3" + XCTAssert(uppercasePitch.key === Key(type: .a, accidental: .sharp)) + XCTAssert(uppercasePitch.octave == 3) + + let uppercasePitch2: Pitch = "F4" + XCTAssert(uppercasePitch2.key === Key(type: .f, accidental: .natural)) + XCTAssert(uppercasePitch2.octave == 4) + } + + func testFrequency() { + let note = Pitch(key: Key(type: .a), octave: 4) + XCTAssertEqual(note.frequency, 440.0) + + let a4 = Pitch.nearest(frequency: 440.0) + XCTAssertEqual(note, a4) + } } // MARK: - Tempo Tests extension MusicTheoryTests { - func testNoteValueConversions() { - var noteValue = NoteValue(type: .half, modifier: .dotted) - XCTAssertEqual(noteValue / NoteValueType.sixteenth, 12) - XCTAssertEqual(noteValue / NoteValueType.whole, 0.75) - noteValue = NoteValue(type: .half, modifier: .default) - XCTAssertEqual(noteValue / NoteValueType.sixteenth, 8) - XCTAssertEqual(noteValue / NoteValueType.whole, 0.5) - XCTAssertEqual(noteValue / NoteValueType.quarter, 2) - XCTAssertEqual(noteValue / NoteValueType.thirtysecond, 16) - } - - func testDurations() { - let timeSignature = TimeSignature(beats: 4, noteValue: .quarter) // 4/4 - let tempo = Tempo(timeSignature: timeSignature, bpm: 120) // 120BPM - var noteValue = NoteValue(type: .quarter) - var duration = tempo.duration(of: noteValue) - XCTAssert(duration == 0.5) - - noteValue.modifier = .dotted - duration = tempo.duration(of: noteValue) - XCTAssert(duration == 0.75) - } - - func testSampleLengthCalcuation() { - let rates = [ - NoteValue(type: .whole, modifier: .default), - NoteValue(type: .half, modifier: .default), - NoteValue(type: .half, modifier: .dotted), - NoteValue(type: .half, modifier: .triplet), - NoteValue(type: .quarter, modifier: .default), - NoteValue(type: .quarter, modifier: .dotted), - NoteValue(type: .quarter, modifier: .triplet), - NoteValue(type: .eighth, modifier: .default), - NoteValue(type: .eighth, modifier: .dotted), - NoteValue(type: .sixteenth, modifier: .default), - NoteValue(type: .sixteenth, modifier: .dotted), - NoteValue(type: .thirtysecond, modifier: .default), - NoteValue(type: .sixtyfourth, modifier: .default), - ] - - let tempo = Tempo() - let sampleLengths = rates - .map({ tempo.sampleLength(of: $0) }) - .map({ round(100 * $0) / 100 }) - - let expected: [Double] = [ - 88200.0, - 44100.0, - 66150.0, - 29401.47, - 22050.0, - 33075.0, - 14700.73, - 11025.0, - 16537.5, - 5512.5, - 8268.75, - 2756.25, - 1378.13, - ] - - XCTAssertEqual(sampleLengths, expected) - } - - func testTempoHashable() { - let t1 = Tempo(timeSignature: TimeSignature(beats: 1, noteValue: .whole), bpm: 1) - var t2 = Tempo(timeSignature: TimeSignature(beats: 2, noteValue: .half), bpm: 2) - XCTAssertNotEqual(t1.timeSignature.noteValue, t2.timeSignature.noteValue) - XCTAssertNotEqual(t1.timeSignature, t2.timeSignature) - XCTAssertNotEqual(t1, t2) - - t2.timeSignature = TimeSignature(beats: 1, noteValue: .whole) - t2.bpm = 1 - XCTAssertEqual(t1.timeSignature.noteValue, t2.timeSignature.noteValue) - XCTAssertEqual(t1.timeSignature, t2.timeSignature) - XCTAssertEqual(t1, t2) - } + func testNoteValueConversions() { + var noteValue = NoteValue(type: .half, modifier: .dotted) + XCTAssertEqual(noteValue / NoteValueType.sixteenth, 12) + XCTAssertEqual(noteValue / NoteValueType.whole, 0.75) + noteValue = NoteValue(type: .half, modifier: .default) + XCTAssertEqual(noteValue / NoteValueType.sixteenth, 8) + XCTAssertEqual(noteValue / NoteValueType.whole, 0.5) + XCTAssertEqual(noteValue / NoteValueType.quarter, 2) + XCTAssertEqual(noteValue / NoteValueType.thirtysecond, 16) + } + + func testDurations() { + let timeSignature = TimeSignature(beats: 4, noteValue: .quarter) // 4/4 + let tempo = Tempo(timeSignature: timeSignature, bpm: 120) // 120BPM + var noteValue = NoteValue(type: .quarter) + var duration = tempo.duration(of: noteValue) + XCTAssert(duration == 0.5) + + noteValue.modifier = .dotted + duration = tempo.duration(of: noteValue) + XCTAssert(duration == 0.75) + } + + func testSampleLengthCalcuation() { + let rates = [ + NoteValue(type: .whole, modifier: .default), + NoteValue(type: .half, modifier: .default), + NoteValue(type: .half, modifier: .dotted), + NoteValue(type: .half, modifier: .triplet), + NoteValue(type: .quarter, modifier: .default), + NoteValue(type: .quarter, modifier: .dotted), + NoteValue(type: .quarter, modifier: .triplet), + NoteValue(type: .eighth, modifier: .default), + NoteValue(type: .eighth, modifier: .dotted), + NoteValue(type: .sixteenth, modifier: .default), + NoteValue(type: .sixteenth, modifier: .dotted), + NoteValue(type: .thirtysecond, modifier: .default), + NoteValue(type: .sixtyfourth, modifier: .default), + ] + + let tempo = Tempo() + let sampleLengths = rates + .map({ tempo.sampleLength(of: $0) }) + .map({ round(100 * $0) / 100 }) + + let expected: [Double] = [ + 88200.0, + 44100.0, + 66150.0, + 29401.47, + 22050.0, + 33075.0, + 14700.73, + 11025.0, + 16537.5, + 5512.5, + 8268.75, + 2756.25, + 1378.13, + ] + + XCTAssertEqual(sampleLengths, expected) + } + + func testTempoHashable() { + let t1 = Tempo(timeSignature: TimeSignature(beats: 1, noteValue: .whole), bpm: 1) + var t2 = Tempo(timeSignature: TimeSignature(beats: 2, noteValue: .half), bpm: 2) + XCTAssertNotEqual(t1.timeSignature.noteValue, t2.timeSignature.noteValue) + XCTAssertNotEqual(t1.timeSignature, t2.timeSignature) + XCTAssertNotEqual(t1, t2) + + t2.timeSignature = TimeSignature(beats: 1, noteValue: .whole) + t2.bpm = 1 + XCTAssertEqual(t1.timeSignature.noteValue, t2.timeSignature.noteValue) + XCTAssertEqual(t1.timeSignature, t2.timeSignature) + XCTAssertEqual(t1, t2) + } } // MARK: - Scale Tests extension MusicTheoryTests { - func testScale() { - let cMaj: [Key] = [ - Key(type: .c), - Key(type: .d), - Key(type: .e), - Key(type: .f), - Key(type: .g), - Key(type: .a), - Key(type: .b), - ] - - let cMajScale = Scale(type: .major, key: Key(type: .c)) - XCTAssert(cMajScale.keys == cMaj) - - let cMin: [Key] = [ - Key(type: .c), - Key(type: .d), - Key(type: .e, accidental: .flat), - Key(type: .f), - Key(type: .g), - Key(type: .a, accidental: .flat), - Key(type: .b, accidental: .flat), - ] - - let cMinScale = Scale(type: .minor, key: Key(type: .c)) - XCTAssert(cMinScale.keys == cMin) - } - - func testHarmonicFields() { - let cmaj = Scale(type: .major, key: Key(type: .c)) - let triads = cmaj.harmonicField(for: .triad) - let triadsExpected = [ - Chord(type: ChordType(third: .major), key: Key(type: .c)), - Chord(type: ChordType(third: .minor), key: Key(type: .d)), - Chord(type: ChordType(third: .minor), key: Key(type: .e)), - Chord(type: ChordType(third: .major), key: Key(type: .f)), - Chord(type: ChordType(third: .major), key: Key(type: .g)), - Chord(type: ChordType(third: .minor), key: Key(type: .a)), - Chord(type: ChordType(third: .minor, fifth: .diminished), key: Key(type: .b)), - ] - XCTAssert(triads.enumerated().map({ $1 == triadsExpected[$0] }).filter({ $0 == false }).count == 0) - } + func testScale() { + let cMaj: [Key] = [ + Key(type: .c), + Key(type: .d), + Key(type: .e), + Key(type: .f), + Key(type: .g), + Key(type: .a), + Key(type: .b), + ] + + let cMajScale = Scale(type: .major, key: Key(type: .c)) + XCTAssert(cMajScale.keys == cMaj) + + let cMin: [Key] = [ + Key(type: .c), + Key(type: .d), + Key(type: .e, accidental: .flat), + Key(type: .f), + Key(type: .g), + Key(type: .a, accidental: .flat), + Key(type: .b, accidental: .flat), + ] + + let cMinScale = Scale(type: .minor, key: Key(type: .c)) + XCTAssert(cMinScale.keys == cMin) + } + + func testHarmonicFields() { + let cmaj = Scale(type: .major, key: Key(type: .c)) + let triads = cmaj.harmonicField(for: .triad) + let triadsExpected = [ + Chord(type: ChordType(third: .major), key: Key(type: .c)), + Chord(type: ChordType(third: .minor), key: Key(type: .d)), + Chord(type: ChordType(third: .minor), key: Key(type: .e)), + Chord(type: ChordType(third: .major), key: Key(type: .f)), + Chord(type: ChordType(third: .major), key: Key(type: .g)), + Chord(type: ChordType(third: .minor), key: Key(type: .a)), + Chord(type: ChordType(third: .minor, fifth: .diminished), key: Key(type: .b)), + ] + XCTAssert(triads.enumerated().map({ $1 == triadsExpected[$0] }).filter({ $0 == false }).count == 0) + } } // MARK: - Chord Tests extension MusicTheoryTests { - func testChords() { - let cmajNotes: [Key] = [Key(type: .c), Key(type: .e), Key(type: .g)] - let cmaj = Chord(type: ChordType(third: .major), key: Key(type: .c)) - XCTAssert(cmajNotes == cmaj.keys) - - let cminNotes: [Key] = [ - Key(type: .c), - Key(type: .e, accidental: .flat), - Key(type: .g), - ] - let cmin = Chord(type: ChordType(third: .minor), key: Key(type: .c)) - XCTAssert(cminNotes == cmin.keys) - - let c13Notes: [Pitch] = [ - Pitch(key: Key(type: .c), octave: 1), - Pitch(key: Key(type: .e), octave: 1), - Pitch(key: Key(type: .g), octave: 1), - Pitch(key: Key(type: .b, accidental: .flat), octave: 1), - Pitch(key: Key(type: .d), octave: 2), - Pitch(key: Key(type: .f), octave: 2), - Pitch(key: Key(type: .a), octave: 2), - ] - let c13 = Chord( - type: ChordType( - third: .major, - seventh: .dominant, - extensions: [ - ChordExtensionType(type: .thirteenth), + func testChords() { + let cmajNotes: [Key] = [Key(type: .c), Key(type: .e), Key(type: .g)] + let cmaj = Chord(type: ChordType(third: .major), key: Key(type: .c)) + XCTAssert(cmajNotes == cmaj.keys) + + let cminNotes: [Key] = [ + Key(type: .c), + Key(type: .e, accidental: .flat), + Key(type: .g), ] - ), - key: Key(type: .c) - ) - XCTAssert(c13.pitches(octave: 1) === c13Notes) - - let cm13Notes: [Pitch] = [ - Pitch(key: Key(type: .c), octave: 1), - Pitch(key: Key(type: .e, accidental: .flat), octave: 1), - Pitch(key: Key(type: .g), octave: 1), - Pitch(key: Key(type: .b, accidental: .flat), octave: 1), - Pitch(key: Key(type: .d), octave: 2), - Pitch(key: Key(type: .f), octave: 2), - Pitch(key: Key(type: .a), octave: 2), - ] - let cm13 = Chord( - type: ChordType( - third: .minor, - seventh: .dominant, - extensions: [ - ChordExtensionType(type: .thirteenth), + let cmin = Chord(type: ChordType(third: .minor), key: Key(type: .c)) + XCTAssert(cminNotes == cmin.keys) + + let c13Notes: [Pitch] = [ + Pitch(key: Key(type: .c), octave: 1), + Pitch(key: Key(type: .e), octave: 1), + Pitch(key: Key(type: .g), octave: 1), + Pitch(key: Key(type: .b, accidental: .flat), octave: 1), + Pitch(key: Key(type: .d), octave: 2), + Pitch(key: Key(type: .f), octave: 2), + Pitch(key: Key(type: .a), octave: 2), ] - ), - key: Key(type: .c) - ) - XCTAssert(cm13.pitches(octave: 1) === cm13Notes) - - let minorIntervals: [Interval] = [.P1, .m3, .P5] - guard let minorChord = ChordType(intervals: minorIntervals.map({ $0 })) else { return XCTFail() } - XCTAssert(minorChord == ChordType(third: .minor)) - - let majorIntervals: [Interval] = [.P1, .M3, .P5] - guard let majorChord = ChordType(intervals: majorIntervals.map({ $0 })) else { return XCTFail() } - XCTAssert(majorChord == ChordType(third: .major)) - - let cmadd13Notes: [Pitch] = [ - Pitch(key: Key(type: .c), octave: 1), - Pitch(key: Key(type: .e, accidental: .flat), octave: 1), - Pitch(key: Key(type: .g), octave: 1), - Pitch(key: Key(type: .a), octave: 2), - ] - let cmadd13 = Chord( - type: ChordType( - third: .minor, - extensions: [ChordExtensionType(type: .thirteenth)] - ), - key: Key(type: .c) - ) - XCTAssert(cmadd13.pitches(octave: 1) === cmadd13Notes) - } - - func testRomanNumerics() { - let cmaj = Scale(type: .major, key: Key(type: .c)) - let cmin = Scale(type: .minor, key: Key(type: .c)) - let cmajNumerics = ["I", "ii", "iii", "IV", "V", "vi", "vii°"] - let cminNumerics = ["i", "ii°", "III", "iv", "v", "VI", "VII"] - let cmajChords = cmaj.harmonicField(for: .triad) - let cminChords = cmin.harmonicField(for: .triad) - XCTAssertEqual(cmajNumerics, cmajChords.compactMap({ $0?.romanNumeric(for: cmaj) })) - XCTAssertEqual(cminNumerics, cminChords.compactMap({ $0?.romanNumeric(for: cmin) })) - } - - func testInversions() { - let c7 = Chord( - type: ChordType(third: .major, seventh: .dominant), - key: Key(type: .c) - ) - let c7Inversions = [ - [ - Pitch(key: Key(type: .c), octave: 1), - Pitch(key: Key(type: .e), octave: 1), - Pitch(key: Key(type: .g), octave: 1), - Pitch(key: Key(type: .b, accidental: .flat), octave: 1), - ], - [ - Pitch(key: Key(type: .e), octave: 1), - Pitch(key: Key(type: .g), octave: 1), - Pitch(key: Key(type: .b, accidental: .flat), octave: 1), - Pitch(key: Key(type: .c), octave: 2), - ], - [ - Pitch(key: Key(type: .g), octave: 1), - Pitch(key: Key(type: .b, accidental: .flat), octave: 1), - Pitch(key: Key(type: .c), octave: 2), - Pitch(key: Key(type: .e), octave: 2), - ], - [ - Pitch(key: Key(type: .b, accidental: .flat), octave: 1), - Pitch(key: Key(type: .c), octave: 2), - Pitch(key: Key(type: .e), octave: 2), - Pitch(key: Key(type: .g), octave: 2), - ], - ] - for (index, chord) in c7.inversions.enumerated() { - XCTAssert(chord.pitches(octave: 1) === c7Inversions[index]) + let c13 = Chord( + type: ChordType( + third: .major, + seventh: .dominant, + extensions: [ + ChordExtensionType(type: .thirteenth), + ] + ), + key: Key(type: .c) + ) + XCTAssert(c13.pitches(octave: 1) === c13Notes) + + let cm13Notes: [Pitch] = [ + Pitch(key: Key(type: .c), octave: 1), + Pitch(key: Key(type: .e, accidental: .flat), octave: 1), + Pitch(key: Key(type: .g), octave: 1), + Pitch(key: Key(type: .b, accidental: .flat), octave: 1), + Pitch(key: Key(type: .d), octave: 2), + Pitch(key: Key(type: .f), octave: 2), + Pitch(key: Key(type: .a), octave: 2), + ] + let cm13 = Chord( + type: ChordType( + third: .minor, + seventh: .dominant, + extensions: [ + ChordExtensionType(type: .thirteenth), + ] + ), + key: Key(type: .c) + ) + XCTAssert(cm13.pitches(octave: 1) === cm13Notes) + + let minorIntervals: [Interval] = [.P1, .m3, .P5] + guard let minorChord = ChordType(intervals: minorIntervals.map({ $0 })) else { return XCTFail() } + XCTAssert(minorChord == ChordType(third: .minor)) + + let majorIntervals: [Interval] = [.P1, .M3, .P5] + guard let majorChord = ChordType(intervals: majorIntervals.map({ $0 })) else { return XCTFail() } + XCTAssert(majorChord == ChordType(third: .major)) + + let cmadd13Notes: [Pitch] = [ + Pitch(key: Key(type: .c), octave: 1), + Pitch(key: Key(type: .e, accidental: .flat), octave: 1), + Pitch(key: Key(type: .g), octave: 1), + Pitch(key: Key(type: .a), octave: 2), + ] + let cmadd13 = Chord( + type: ChordType( + third: .minor, + extensions: [ChordExtensionType(type: .thirteenth)] + ), + key: Key(type: .c) + ) + XCTAssert(cmadd13.pitches(octave: 1) === cmadd13Notes) + } + + func testRomanNumerics() { + let cmaj = Scale(type: .major, key: Key(type: .c)) + let cmin = Scale(type: .minor, key: Key(type: .c)) + let cmajNumerics = ["I", "ii", "iii", "IV", "V", "vi", "vii°"] + let cminNumerics = ["i", "ii°", "III", "iv", "v", "VI", "VII"] + let cmajChords = cmaj.harmonicField(for: .triad) + let cminChords = cmin.harmonicField(for: .triad) + XCTAssertEqual(cmajNumerics, cmajChords.compactMap({ $0?.romanNumeric(for: cmaj) })) + XCTAssertEqual(cminNumerics, cminChords.compactMap({ $0?.romanNumeric(for: cmin) })) + } + + func testInversions() { + let c7 = Chord( + type: ChordType(third: .major, seventh: .dominant), + key: Key(type: .c) + ) + let c7Inversions = [ + [ + Pitch(key: Key(type: .c), octave: 1), + Pitch(key: Key(type: .e), octave: 1), + Pitch(key: Key(type: .g), octave: 1), + Pitch(key: Key(type: .b, accidental: .flat), octave: 1), + ], + [ + Pitch(key: Key(type: .e), octave: 1), + Pitch(key: Key(type: .g), octave: 1), + Pitch(key: Key(type: .b, accidental: .flat), octave: 1), + Pitch(key: Key(type: .c), octave: 2), + ], + [ + Pitch(key: Key(type: .g), octave: 1), + Pitch(key: Key(type: .b, accidental: .flat), octave: 1), + Pitch(key: Key(type: .c), octave: 2), + Pitch(key: Key(type: .e), octave: 2), + ], + [ + Pitch(key: Key(type: .b, accidental: .flat), octave: 1), + Pitch(key: Key(type: .c), octave: 2), + Pitch(key: Key(type: .e), octave: 2), + Pitch(key: Key(type: .g), octave: 2), + ], + ] + for (index, chord) in c7.inversions.enumerated() { + XCTAssert(chord.pitches(octave: 1) === c7Inversions[index]) + } } - } } // MARK: - [Pitch] Extension // A function for checking pitche arrays exactly equal in terms of their pitches keys and octaves. func === (lhs: [Pitch], rhs: [Pitch]) -> Bool { - guard lhs.count == rhs.count else { return false } - for i in 0 ..< lhs.count { - if lhs[i] === rhs[i] { - continue - } else { - return false + guard lhs.count == rhs.count else { return false } + for i in 0 ..< lhs.count { + if lhs[i] === rhs[i] { + continue + } else { + return false + } } - } - return true + return true } From de02a1b54b31b82c22493d18ea242c13023d55a6 Mon Sep 17 00:00:00 2001 From: Cem Olcay Date: Sat, 13 Jul 2024 11:45:41 +0300 Subject: [PATCH 3/8] Update NoteValue.swift --- Sources/MusicTheory/NoteValue.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/MusicTheory/NoteValue.swift b/Sources/MusicTheory/NoteValue.swift index 39ce181..61dddea 100644 --- a/Sources/MusicTheory/NoteValue.swift +++ b/Sources/MusicTheory/NoteValue.swift @@ -19,7 +19,6 @@ public struct NoteValueType: Codable, Hashable, CustomStringConvertible { /// Name of the note value. public var description: String - /// Sixteen bar notes. public static let sixteenBars = NoteValueType(beats: 16.0, description: "16 Bars") /// Eigth bar notes. From 7e38e5d4a00e59ef94957ff87b3d31a1bf98f7cb Mon Sep 17 00:00:00 2001 From: Cem Olcay Date: Sat, 13 Jul 2024 11:53:42 +0300 Subject: [PATCH 4/8] Update playground --- .../Contents.swift | 52 +++++++++---------- Playground.playground/contents.xcplayground | 2 +- Sources/MusicTheory/HarmonicFunctions.swift | 1 - 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/Playground.playground/Pages/Untitled Page.xcplaygroundpage/Contents.swift b/Playground.playground/Pages/Untitled Page.xcplaygroundpage/Contents.swift index d7b84b3..a96ef76 100644 --- a/Playground.playground/Pages/Untitled Page.xcplaygroundpage/Contents.swift +++ b/Playground.playground/Pages/Untitled Page.xcplaygroundpage/Contents.swift @@ -31,35 +31,35 @@ print(aminPtriad) // chord progression for C# harmonic minor triads let progression = ChordProgression.i_ii_vi_iv let cSharpHarmonicMinorTriadsProgression = progression.chords( - for: cSharpHarmonicMinor, - harmonicField: .triad, - inversion: 0 + for: cSharpHarmonicMinor, + harmonicField: .triad, + inversion: 0 ) print(cSharpHarmonicMinorTriadsProgression) let c13 = Chord( - type: ChordType( - third: .major, - fifth: .perfect, - sixth: nil, - seventh: .dominant, - suspended: nil, - extensions: [ - ChordExtensionType(type: .thirteenth, accidental: .natural), - ] - ), - key: Key( - type: .c, - accidental: .natural - ) + type: ChordType( + third: .major, + fifth: .perfect, + sixth: nil, + seventh: .dominant, + suspended: nil, + extensions: [ + ChordExtensionType(type: .thirteenth, accidental: .natural), + ] + ), + key: Key( + type: .c, + accidental: .natural + ) ) let cdim7 = Chord( - type: ChordType( - third: .major, - fifth: .diminished, - seventh: .diminished), - key: Key(type: .c)) + type: ChordType( + third: .major, + fifth: .diminished, + seventh: .diminished), + key: Key(type: .c)) cdim7.notation print(cdim7.keys) @@ -90,10 +90,10 @@ let aHarmonicMinor = Scale(type: .harmonicMinor, key: Key(type: .a)) print(aHarmonicMinor) let harmonicFunctions = HarmonicFunctions(scale: aHarmonicMinor) HarmonicFunctionType.allCases.forEach({ type in - let relatedKeys = type.direction.map({ related in - harmonicFunctions.harmonicFunction(for: related)! - }) - print(type, relatedKeys) + let relatedKeys = type.direction.map({ related in + harmonicFunctions.harmonicFunction(for: related)! + }) + print(type, relatedKeys) }) func nearestKey(key: Key, scale: Scale) -> Key { diff --git a/Playground.playground/contents.xcplayground b/Playground.playground/contents.xcplayground index 3debe4b..466e35c 100644 --- a/Playground.playground/contents.xcplayground +++ b/Playground.playground/contents.xcplayground @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/Sources/MusicTheory/HarmonicFunctions.swift b/Sources/MusicTheory/HarmonicFunctions.swift index a25fd2a..9f6e184 100644 --- a/Sources/MusicTheory/HarmonicFunctions.swift +++ b/Sources/MusicTheory/HarmonicFunctions.swift @@ -8,7 +8,6 @@ // https://github.com/cemolcay/MusicTheory // - import Foundation /// Represents harmonic functions in music theory. From da9da41cade24c0d1b8fe71a38e9103cb281a342 Mon Sep 17 00:00:00 2001 From: Cem Olcay Date: Sat, 13 Jul 2024 11:56:33 +0300 Subject: [PATCH 5/8] Update NoteValue.swift --- Sources/MusicTheory/NoteValue.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/MusicTheory/NoteValue.swift b/Sources/MusicTheory/NoteValue.swift index 61dddea..b5d7fa4 100644 --- a/Sources/MusicTheory/NoteValue.swift +++ b/Sources/MusicTheory/NoteValue.swift @@ -105,6 +105,11 @@ public struct NoteValue: Codable, CustomStringConvertible { self.modifier = modifier } + /// Note value in beats. + public var beats: Double { + return type.beats * modifier.rawValue + } + /// Returns the string representation of the note value. public var description: String { return "\(type)\(modifier)" From 84d9926aac5049e21c0b06f0e53fae698ccf07e7 Mon Sep 17 00:00:00 2001 From: Cem Olcay Date: Sat, 13 Jul 2024 11:59:21 +0300 Subject: [PATCH 6/8] Update NoteValue.swift --- Sources/MusicTheory/NoteValue.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Sources/MusicTheory/NoteValue.swift b/Sources/MusicTheory/NoteValue.swift index b5d7fa4..0c96c01 100644 --- a/Sources/MusicTheory/NoteValue.swift +++ b/Sources/MusicTheory/NoteValue.swift @@ -88,8 +88,18 @@ public func / (noteValue: NoteValue, noteValueType: NoteValueType) -> Double { return (noteValue.type.beats * noteValue.modifier.rawValue) / noteValueType.beats } +/// Checks the equality between two `NoteValue` types. +/// +/// - Parameters: +/// - lhs: Left hand side `NoteValue`. +/// - rhs: Right hand side `NoteValue`. +/// - Returns: Returns true if two `NoteValue`s are equal. +public func == (lhs: NoteValue, rhs: NoteValue) -> Bool { + return lhs.type == rhs.type && lhs.modifier == rhs.modifier +} + /// Defines the duration of a note beatwise. -public struct NoteValue: Codable, CustomStringConvertible { +public struct NoteValue: Codable, Equatable, CustomStringConvertible { /// Type that represents the duration of note. public var type: NoteValueType /// Modifier for `NoteType` that modifies the duration. @@ -115,3 +125,4 @@ public struct NoteValue: Codable, CustomStringConvertible { return "\(type)\(modifier)" } } + From f28831b1df8602330c74a0272766599ee6e57270 Mon Sep 17 00:00:00 2001 From: Cem Olcay Date: Sat, 13 Jul 2024 12:05:06 +0300 Subject: [PATCH 7/8] Update NoteValue.swift --- Sources/MusicTheory/NoteValue.swift | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Sources/MusicTheory/NoteValue.swift b/Sources/MusicTheory/NoteValue.swift index 0c96c01..92c3846 100644 --- a/Sources/MusicTheory/NoteValue.swift +++ b/Sources/MusicTheory/NoteValue.swift @@ -19,6 +19,16 @@ public struct NoteValueType: Codable, Hashable, CustomStringConvertible { /// Name of the note value. public var description: String + /// Creates a note value type. + /// + /// - Parameters: + /// - beats: The rate of the note value in beats. + /// - description: The name of the note value. + public init(beats: Double, description: String) { + self.beats = beats + self.description = description + } + /// Sixteen bar notes. public static let sixteenBars = NoteValueType(beats: 16.0, description: "16 Bars") /// Eigth bar notes. @@ -89,7 +99,7 @@ public func / (noteValue: NoteValue, noteValueType: NoteValueType) -> Double { } /// Checks the equality between two `NoteValue` types. -/// +/// /// - Parameters: /// - lhs: Left hand side `NoteValue`. /// - rhs: Right hand side `NoteValue`. From 3cfca3d18f22d77f5d1e380244ae918bca96c0a2 Mon Sep 17 00:00:00 2001 From: Cem Olcay Date: Thu, 18 Jul 2024 17:40:54 +0300 Subject: [PATCH 8/8] Bring back the breaking rename change --- Sources/MusicTheory/NoteValue.swift | 42 ++++++++++++------------- Sources/MusicTheory/Tempo.swift | 4 +-- Sources/MusicTheory/TimeSignature.swift | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Sources/MusicTheory/NoteValue.swift b/Sources/MusicTheory/NoteValue.swift index 92c3846..d547958 100644 --- a/Sources/MusicTheory/NoteValue.swift +++ b/Sources/MusicTheory/NoteValue.swift @@ -14,47 +14,47 @@ import Foundation /// Defines the types of note values. public struct NoteValueType: Codable, Hashable, CustomStringConvertible { - /// Note value in beats. - public var beats: Double + /// The note value's duration in beats. + public var rate: Double /// Name of the note value. public var description: String /// Creates a note value type. /// /// - Parameters: - /// - beats: The rate of the note value in beats. + /// - rate: The rate of the note value in beats. /// - description: The name of the note value. - public init(beats: Double, description: String) { - self.beats = beats + public init(rate: Double, description: String) { + self.rate = rate self.description = description } /// Sixteen bar notes. - public static let sixteenBars = NoteValueType(beats: 16.0, description: "16 Bars") + public static let sixteenBars = NoteValueType(rate: 16.0, description: "16 Bars") /// Eigth bar notes. - public static let eigthBars = NoteValueType(beats: 8.0, description: "8 Bars") + public static let eigthBars = NoteValueType(rate: 8.0, description: "8 Bars") /// Four bar notes. - public static let fourBars = NoteValueType(beats: 4.0, description: "4 Bars") + public static let fourBars = NoteValueType(rate: 4.0, description: "4 Bars") /// Two bar notes. - public static let twoBars = NoteValueType(beats: 2.0, description: "2 Bars") + public static let twoBars = NoteValueType(rate: 2.0, description: "2 Bars") /// One bar note. - public static let oneBar = NoteValueType(beats: 1.0, description: "1 Bar") + public static let oneBar = NoteValueType(rate: 1.0, description: "1 Bar") /// Two whole notes. - public static let doubleWhole = NoteValueType(beats: 2.0 / 1.0, description: "2/1") + public static let doubleWhole = NoteValueType(rate: 2.0 / 1.0, description: "2/1") /// Whole note. - public static let whole = NoteValueType(beats: 1.0 / 1.0, description: "1/1") + public static let whole = NoteValueType(rate: 1.0 / 1.0, description: "1/1") /// Half note. - public static let half = NoteValueType(beats: 1.0 / 2.0, description: "1/2") + public static let half = NoteValueType(rate: 1.0 / 2.0, description: "1/2") /// Quarter note. - public static let quarter = NoteValueType(beats: 1.0 / 4.0, description: "1/4") + public static let quarter = NoteValueType(rate: 1.0 / 4.0, description: "1/4") /// Eighth note. - public static let eighth = NoteValueType(beats: 1.0 / 8.0, description: "1/8") + public static let eighth = NoteValueType(rate: 1.0 / 8.0, description: "1/8") /// Sixteenth note. - public static let sixteenth = NoteValueType(beats: 1.0 / 16.0, description: "1/16") + public static let sixteenth = NoteValueType(rate: 1.0 / 16.0, description: "1/16") /// Thirtysecond note. - public static let thirtysecond = NoteValueType(beats: 1.0 / 32.0, description: "1/32") + public static let thirtysecond = NoteValueType(rate: 1.0 / 32.0, description: "1/32") /// Sixtyfourth note. - public static let sixtyfourth = NoteValueType(beats: 1.0 / 64.0, description: "1/64") + public static let sixtyfourth = NoteValueType(rate: 1.0 / 64.0, description: "1/64") public static let all: [NoteValueType] = [ .sixteenBars, .eigthBars, .fourBars, .twoBars, .oneBar, @@ -95,7 +95,7 @@ public enum NoteModifier: Double, Codable, CaseIterable, CustomStringConvertible /// - noteValueType: The note value type to measure the length of the note value. /// - Returns: Returns how many notes of a single `NoteValueType` is equivalent to a given `NoteValue`. public func / (noteValue: NoteValue, noteValueType: NoteValueType) -> Double { - return (noteValue.type.beats * noteValue.modifier.rawValue) / noteValueType.beats + return (noteValue.type.rate * noteValue.modifier.rawValue) / noteValueType.rate } /// Checks the equality between two `NoteValue` types. @@ -126,8 +126,8 @@ public struct NoteValue: Codable, Equatable, CustomStringConvertible { } /// Note value in beats. - public var beats: Double { - return type.beats * modifier.rawValue + public var rate: Double { + return type.rate * modifier.rawValue } /// Returns the string representation of the note value. diff --git a/Sources/MusicTheory/Tempo.swift b/Sources/MusicTheory/Tempo.swift index d3c5c3d..0ccf2e6 100644 --- a/Sources/MusicTheory/Tempo.swift +++ b/Sources/MusicTheory/Tempo.swift @@ -30,7 +30,7 @@ public struct Tempo: Codable, Hashable, CustomStringConvertible { /// Caluclates the duration of a note value in seconds. public func duration(of noteValue: NoteValue) -> TimeInterval { let secondsPerBeat = 60.0 / bpm - return secondsPerBeat * (timeSignature.noteValue.beats / noteValue.type.beats) * noteValue.modifier.rawValue + return secondsPerBeat * (timeSignature.noteValue.rate / noteValue.type.rate) * noteValue.modifier.rawValue } /// Calculates the note length in samples. Useful for sequencing notes sample accurate in the DSP. @@ -41,7 +41,7 @@ public struct Tempo: Codable, Hashable, CustomStringConvertible { /// - Returns: Returns the sample length of a note value. public func sampleLength(of noteValue: NoteValue, sampleRate: Double = 44100.0) -> Double { let secondsPerBeat = 60.0 / bpm - return secondsPerBeat * sampleRate * ((Double(timeSignature.beats) * noteValue.type.beats) * noteValue.modifier.rawValue) + return secondsPerBeat * sampleRate * ((Double(timeSignature.beats) * noteValue.type.rate) * noteValue.modifier.rawValue) } /// Calculates the LFO speed of a note vaule in hertz. diff --git a/Sources/MusicTheory/TimeSignature.swift b/Sources/MusicTheory/TimeSignature.swift index add48bc..4107e8b 100644 --- a/Sources/MusicTheory/TimeSignature.swift +++ b/Sources/MusicTheory/TimeSignature.swift @@ -30,6 +30,6 @@ public struct TimeSignature: Codable, Hashable, CustomStringConvertible { // MARK: CustomStringConvertible public var description: String { - return "\(beats)/\(Int(noteValue.beats))" + return "\(beats)/\(Int(noteValue.rate))" } }