diff --git a/Swift Musicology/.DS_Store b/Swift Musicology/.DS_Store new file mode 100644 index 0000000..9d61b84 Binary files /dev/null and b/Swift Musicology/.DS_Store differ diff --git a/Swift Musicology/Swift Musicology.xcodeproj/project.pbxproj b/Swift Musicology/Swift Musicology.xcodeproj/project.pbxproj index 136096e..27d01a5 100644 --- a/Swift Musicology/Swift Musicology.xcodeproj/project.pbxproj +++ b/Swift Musicology/Swift Musicology.xcodeproj/project.pbxproj @@ -7,37 +7,95 @@ objects = { /* Begin PBXBuildFile section */ - 1207E0CF21AAE05300915CCD /* Swift_Musicology.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1207E0C521AAE05300915CCD /* Swift_Musicology.framework */; }; + 0BFB65E1245DBD280019EBCB /* SwiftMusicology_iOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BFB65DF245DBD280019EBCB /* SwiftMusicology_iOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0BFB65EE245DBD380019EBCB /* SwiftMusicology_watchOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BFB65EC245DBD380019EBCB /* SwiftMusicology_watchOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0BFB65FB245DBD420019EBCB /* SwiftMusicology_tvOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BFB65F9245DBD420019EBCB /* SwiftMusicology_tvOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0BFB6608245DBD500019EBCB /* SwiftMusicology_macOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BFB6606245DBD500019EBCB /* SwiftMusicology_macOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0BFB660C245DBDD10019EBCB /* Info-iOS.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B6245DBC0E0019EBCB /* Info-iOS.plist */; }; + 0BFB660D245DBDD10019EBCB /* Info-Mac.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B7245DBC0E0019EBCB /* Info-Mac.plist */; }; + 0BFB660E245DBDD10019EBCB /* Info-TV.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B8245DBC0E0019EBCB /* Info-TV.plist */; }; + 0BFB660F245DBDD10019EBCB /* Info-Watch.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B9245DBC0E0019EBCB /* Info-Watch.plist */; }; + 0BFB6610245DBDE40019EBCB /* NoteValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0DF21AAE09500915CCD /* NoteValue.swift */; }; + 0BFB6611245DBDE40019EBCB /* TimeSignature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E121AAE0D100915CCD /* TimeSignature.swift */; }; + 0BFB6612245DBDE40019EBCB /* Tempo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E321AAE10E00915CCD /* Tempo.swift */; }; + 0BFB6613245DBDE40019EBCB /* Accidental.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E521AAE13E00915CCD /* Accidental.swift */; }; + 0BFB6614245DBDE40019EBCB /* Interval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E721AAE15100915CCD /* Interval.swift */; }; + 0BFB6615245DBDE40019EBCB /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E921AAE15700915CCD /* Key.swift */; }; + 0BFB6616245DBDE40019EBCB /* Pitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0EB21AAE16000915CCD /* Pitch.swift */; }; + 0BFB6617245DBDE40019EBCB /* Chord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0ED21AAE16600915CCD /* Chord.swift */; }; + 0BFB6618245DBDE40019EBCB /* ChordProgression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0EF21AAE17000915CCD /* ChordProgression.swift */; }; + 0BFB6619245DBDE40019EBCB /* Scale.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0F121AAE17B00915CCD /* Scale.swift */; }; + 0BFB661A245DBDE40019EBCB /* ScaleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0F521AAEF4800915CCD /* ScaleType.swift */; }; + 0BFB661B245DBE060019EBCB /* NoteValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0DF21AAE09500915CCD /* NoteValue.swift */; }; + 0BFB661C245DBE060019EBCB /* TimeSignature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E121AAE0D100915CCD /* TimeSignature.swift */; }; + 0BFB661D245DBE060019EBCB /* Tempo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E321AAE10E00915CCD /* Tempo.swift */; }; + 0BFB661E245DBE060019EBCB /* Accidental.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E521AAE13E00915CCD /* Accidental.swift */; }; + 0BFB661F245DBE060019EBCB /* Interval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E721AAE15100915CCD /* Interval.swift */; }; + 0BFB6620245DBE060019EBCB /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E921AAE15700915CCD /* Key.swift */; }; + 0BFB6621245DBE060019EBCB /* Pitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0EB21AAE16000915CCD /* Pitch.swift */; }; + 0BFB6622245DBE060019EBCB /* Chord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0ED21AAE16600915CCD /* Chord.swift */; }; + 0BFB6623245DBE060019EBCB /* ChordProgression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0EF21AAE17000915CCD /* ChordProgression.swift */; }; + 0BFB6624245DBE060019EBCB /* Scale.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0F121AAE17B00915CCD /* Scale.swift */; }; + 0BFB6625245DBE060019EBCB /* ScaleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0F521AAEF4800915CCD /* ScaleType.swift */; }; + 0BFB6626245DBE1B0019EBCB /* Info-iOS.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B6245DBC0E0019EBCB /* Info-iOS.plist */; }; + 0BFB6627245DBE1B0019EBCB /* Info-Mac.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B7245DBC0E0019EBCB /* Info-Mac.plist */; }; + 0BFB6628245DBE1B0019EBCB /* Info-TV.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B8245DBC0E0019EBCB /* Info-TV.plist */; }; + 0BFB6629245DBE1B0019EBCB /* Info-Watch.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B9245DBC0E0019EBCB /* Info-Watch.plist */; }; + 0BFB662A245DBE220019EBCB /* Info-iOS.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B6245DBC0E0019EBCB /* Info-iOS.plist */; }; + 0BFB662B245DBE220019EBCB /* Info-Mac.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B7245DBC0E0019EBCB /* Info-Mac.plist */; }; + 0BFB662C245DBE220019EBCB /* Info-TV.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B8245DBC0E0019EBCB /* Info-TV.plist */; }; + 0BFB662D245DBE220019EBCB /* Info-Watch.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B9245DBC0E0019EBCB /* Info-Watch.plist */; }; + 0BFB662E245DBE270019EBCB /* NoteValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0DF21AAE09500915CCD /* NoteValue.swift */; }; + 0BFB662F245DBE270019EBCB /* TimeSignature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E121AAE0D100915CCD /* TimeSignature.swift */; }; + 0BFB6630245DBE270019EBCB /* Tempo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E321AAE10E00915CCD /* Tempo.swift */; }; + 0BFB6631245DBE270019EBCB /* Accidental.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E521AAE13E00915CCD /* Accidental.swift */; }; + 0BFB6632245DBE270019EBCB /* Interval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E721AAE15100915CCD /* Interval.swift */; }; + 0BFB6633245DBE270019EBCB /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E921AAE15700915CCD /* Key.swift */; }; + 0BFB6634245DBE270019EBCB /* Pitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0EB21AAE16000915CCD /* Pitch.swift */; }; + 0BFB6635245DBE270019EBCB /* Chord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0ED21AAE16600915CCD /* Chord.swift */; }; + 0BFB6636245DBE270019EBCB /* ChordProgression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0EF21AAE17000915CCD /* ChordProgression.swift */; }; + 0BFB6637245DBE270019EBCB /* Scale.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0F121AAE17B00915CCD /* Scale.swift */; }; + 0BFB6638245DBE270019EBCB /* ScaleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0F521AAEF4800915CCD /* ScaleType.swift */; }; + 0BFB6639245DBE2D0019EBCB /* NoteValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0DF21AAE09500915CCD /* NoteValue.swift */; }; + 0BFB663A245DBE2D0019EBCB /* TimeSignature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E121AAE0D100915CCD /* TimeSignature.swift */; }; + 0BFB663B245DBE2D0019EBCB /* Tempo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E321AAE10E00915CCD /* Tempo.swift */; }; + 0BFB663C245DBE2D0019EBCB /* Accidental.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E521AAE13E00915CCD /* Accidental.swift */; }; + 0BFB663D245DBE2D0019EBCB /* Interval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E721AAE15100915CCD /* Interval.swift */; }; + 0BFB663E245DBE2D0019EBCB /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E921AAE15700915CCD /* Key.swift */; }; + 0BFB663F245DBE2D0019EBCB /* Pitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0EB21AAE16000915CCD /* Pitch.swift */; }; + 0BFB6640245DBE2D0019EBCB /* Chord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0ED21AAE16600915CCD /* Chord.swift */; }; + 0BFB6641245DBE2D0019EBCB /* ChordProgression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0EF21AAE17000915CCD /* ChordProgression.swift */; }; + 0BFB6642245DBE2D0019EBCB /* Scale.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0F121AAE17B00915CCD /* Scale.swift */; }; + 0BFB6643245DBE2D0019EBCB /* ScaleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0F521AAEF4800915CCD /* ScaleType.swift */; }; + 0BFB6644245DBE320019EBCB /* Info-iOS.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B6245DBC0E0019EBCB /* Info-iOS.plist */; }; + 0BFB6645245DBE320019EBCB /* Info-Mac.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B7245DBC0E0019EBCB /* Info-Mac.plist */; }; + 0BFB6646245DBE320019EBCB /* Info-TV.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B8245DBC0E0019EBCB /* Info-TV.plist */; }; + 0BFB6647245DBE320019EBCB /* Info-Watch.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0BFB65B9245DBC0E0019EBCB /* Info-Watch.plist */; }; 1207E0D421AAE05300915CCD /* Swift_MusicologyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0D321AAE05300915CCD /* Swift_MusicologyTests.swift */; }; - 1207E0D621AAE05300915CCD /* Swift_Musicology.h in Headers */ = {isa = PBXBuildFile; fileRef = 1207E0C821AAE05300915CCD /* Swift_Musicology.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 1207E0E021AAE09500915CCD /* NoteValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0DF21AAE09500915CCD /* NoteValue.swift */; }; - 1207E0E221AAE0D100915CCD /* TimeSignature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E121AAE0D100915CCD /* TimeSignature.swift */; }; - 1207E0E421AAE10E00915CCD /* Tempo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E321AAE10E00915CCD /* Tempo.swift */; }; - 1207E0E621AAE13E00915CCD /* Accidental.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E521AAE13E00915CCD /* Accidental.swift */; }; - 1207E0E821AAE15100915CCD /* Interval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E721AAE15100915CCD /* Interval.swift */; }; - 1207E0EA21AAE15700915CCD /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0E921AAE15700915CCD /* Key.swift */; }; - 1207E0EC21AAE16000915CCD /* Pitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0EB21AAE16000915CCD /* Pitch.swift */; }; - 1207E0EE21AAE16600915CCD /* Chord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0ED21AAE16600915CCD /* Chord.swift */; }; - 1207E0F021AAE17000915CCD /* Chord Progression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0EF21AAE17000915CCD /* Chord Progression.swift */; }; - 1207E0F221AAE17B00915CCD /* Scale.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0F121AAE17B00915CCD /* Scale.swift */; }; - 1207E0F421AAE24300915CCD /* MusicTheoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0F321AAE24300915CCD /* MusicTheoryTests.swift */; }; - 1207E0F621AAEF4800915CCD /* ScaleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1207E0F521AAEF4800915CCD /* ScaleType.swift */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 1207E0D021AAE05300915CCD /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 1207E0BC21AAE05300915CCD /* Project object */; - proxyType = 1; - remoteGlobalIDString = 1207E0C421AAE05300915CCD; - remoteInfo = "Swift Musicology"; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXFileReference section */ - 1207E0C521AAE05300915CCD /* Swift_Musicology.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Swift_Musicology.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0BFB65B6245DBC0E0019EBCB /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; + 0BFB65B7245DBC0E0019EBCB /* Info-Mac.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-Mac.plist"; sourceTree = ""; }; + 0BFB65B8245DBC0E0019EBCB /* Info-TV.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-TV.plist"; sourceTree = ""; }; + 0BFB65B9245DBC0E0019EBCB /* Info-Watch.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-Watch.plist"; sourceTree = ""; }; + 0BFB65C5245DBCF90019EBCB /* SwiftMusicology.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftMusicology.h; sourceTree = ""; }; + 0BFB65C6245DBCF90019EBCB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0BFB65D2245DBD0F0019EBCB /* SwiftMusicology.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftMusicology.h; sourceTree = ""; }; + 0BFB65D3245DBD0F0019EBCB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0BFB65DD245DBD280019EBCB /* SwiftMusicology_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftMusicology_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0BFB65DF245DBD280019EBCB /* SwiftMusicology_iOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftMusicology_iOS.h; sourceTree = ""; }; + 0BFB65E0245DBD280019EBCB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0BFB65EA245DBD380019EBCB /* SwiftMusicology_watchOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftMusicology_watchOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0BFB65EC245DBD380019EBCB /* SwiftMusicology_watchOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftMusicology_watchOS.h; sourceTree = ""; }; + 0BFB65ED245DBD380019EBCB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0BFB65F7245DBD420019EBCB /* SwiftMusicology_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftMusicology_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0BFB65F9245DBD420019EBCB /* SwiftMusicology_tvOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftMusicology_tvOS.h; sourceTree = ""; }; + 0BFB65FA245DBD420019EBCB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0BFB6604245DBD500019EBCB /* SwiftMusicology_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftMusicology_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0BFB6606245DBD500019EBCB /* SwiftMusicology_macOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftMusicology_macOS.h; sourceTree = ""; }; + 0BFB6607245DBD500019EBCB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 1207E0C821AAE05300915CCD /* Swift_Musicology.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Swift_Musicology.h; sourceTree = ""; }; - 1207E0C921AAE05300915CCD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 1207E0CE21AAE05300915CCD /* Swift MusicologyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Swift MusicologyTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 1207E0D321AAE05300915CCD /* Swift_MusicologyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Swift_MusicologyTests.swift; sourceTree = ""; }; 1207E0D521AAE05300915CCD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -49,14 +107,34 @@ 1207E0E921AAE15700915CCD /* Key.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Key.swift; sourceTree = ""; }; 1207E0EB21AAE16000915CCD /* Pitch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pitch.swift; sourceTree = ""; }; 1207E0ED21AAE16600915CCD /* Chord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Chord.swift; sourceTree = ""; }; - 1207E0EF21AAE17000915CCD /* Chord Progression.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Chord Progression.swift"; sourceTree = ""; }; + 1207E0EF21AAE17000915CCD /* ChordProgression.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChordProgression.swift; sourceTree = ""; }; 1207E0F121AAE17B00915CCD /* Scale.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scale.swift; sourceTree = ""; }; - 1207E0F321AAE24300915CCD /* MusicTheoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MusicTheoryTests.swift; sourceTree = ""; }; 1207E0F521AAEF4800915CCD /* ScaleType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaleType.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 1207E0C221AAE05300915CCD /* Frameworks */ = { + 0BFB65DA245DBD280019EBCB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0BFB65E7245DBD380019EBCB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0BFB65F4245DBD420019EBCB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0BFB6601245DBD500019EBCB /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( @@ -67,18 +145,77 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 1207E0CF21AAE05300915CCD /* Swift_Musicology.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 0BFB65C4245DBCF90019EBCB /* SwiftMusicology */ = { + isa = PBXGroup; + children = ( + 0BFB65C5245DBCF90019EBCB /* SwiftMusicology.h */, + 0BFB65C6245DBCF90019EBCB /* Info.plist */, + ); + path = SwiftMusicology; + sourceTree = ""; + }; + 0BFB65D1245DBD0F0019EBCB /* SwiftMusicology */ = { + isa = PBXGroup; + children = ( + 0BFB65D2245DBD0F0019EBCB /* SwiftMusicology.h */, + 0BFB65D3245DBD0F0019EBCB /* Info.plist */, + ); + path = SwiftMusicology; + sourceTree = ""; + }; + 0BFB65DE245DBD280019EBCB /* SwiftMusicology iOS */ = { + isa = PBXGroup; + children = ( + 0BFB65DF245DBD280019EBCB /* SwiftMusicology_iOS.h */, + 0BFB65E0245DBD280019EBCB /* Info.plist */, + ); + path = "SwiftMusicology iOS"; + sourceTree = ""; + }; + 0BFB65EB245DBD380019EBCB /* SwiftMusicology watchOS */ = { + isa = PBXGroup; + children = ( + 0BFB65EC245DBD380019EBCB /* SwiftMusicology_watchOS.h */, + 0BFB65ED245DBD380019EBCB /* Info.plist */, + ); + path = "SwiftMusicology watchOS"; + sourceTree = ""; + }; + 0BFB65F8245DBD420019EBCB /* SwiftMusicology tvOS */ = { + isa = PBXGroup; + children = ( + 0BFB65F9245DBD420019EBCB /* SwiftMusicology_tvOS.h */, + 0BFB65FA245DBD420019EBCB /* Info.plist */, + ); + path = "SwiftMusicology tvOS"; + sourceTree = ""; + }; + 0BFB6605245DBD500019EBCB /* SwiftMusicology macOS */ = { + isa = PBXGroup; + children = ( + 0BFB6606245DBD500019EBCB /* SwiftMusicology_macOS.h */, + 0BFB6607245DBD500019EBCB /* Info.plist */, + ); + path = "SwiftMusicology macOS"; + sourceTree = ""; + }; 1207E0BB21AAE05300915CCD = { isa = PBXGroup; children = ( 1207E0C721AAE05300915CCD /* Swift Musicology */, 1207E0D221AAE05300915CCD /* Swift MusicologyTests */, + 0BFB65C4245DBCF90019EBCB /* SwiftMusicology */, + 0BFB65D1245DBD0F0019EBCB /* SwiftMusicology */, + 0BFB65DE245DBD280019EBCB /* SwiftMusicology iOS */, + 0BFB65EB245DBD380019EBCB /* SwiftMusicology watchOS */, + 0BFB65F8245DBD420019EBCB /* SwiftMusicology tvOS */, + 0BFB6605245DBD500019EBCB /* SwiftMusicology macOS */, 1207E0C621AAE05300915CCD /* Products */, ); sourceTree = ""; @@ -86,8 +223,11 @@ 1207E0C621AAE05300915CCD /* Products */ = { isa = PBXGroup; children = ( - 1207E0C521AAE05300915CCD /* Swift_Musicology.framework */, 1207E0CE21AAE05300915CCD /* Swift MusicologyTests.xctest */, + 0BFB65DD245DBD280019EBCB /* SwiftMusicology_iOS.framework */, + 0BFB65EA245DBD380019EBCB /* SwiftMusicology_watchOS.framework */, + 0BFB65F7245DBD420019EBCB /* SwiftMusicology_tvOS.framework */, + 0BFB6604245DBD500019EBCB /* SwiftMusicology_macOS.framework */, ); name = Products; sourceTree = ""; @@ -95,19 +235,22 @@ 1207E0C721AAE05300915CCD /* Swift Musicology */ = { isa = PBXGroup; children = ( - 1207E0C821AAE05300915CCD /* Swift_Musicology.h */, - 1207E0C921AAE05300915CCD /* Info.plist */, - 1207E0DF21AAE09500915CCD /* NoteValue.swift */, - 1207E0E121AAE0D100915CCD /* TimeSignature.swift */, - 1207E0E321AAE10E00915CCD /* Tempo.swift */, 1207E0E521AAE13E00915CCD /* Accidental.swift */, + 1207E0ED21AAE16600915CCD /* Chord.swift */, + 1207E0EF21AAE17000915CCD /* ChordProgression.swift */, + 0BFB65B6245DBC0E0019EBCB /* Info-iOS.plist */, + 0BFB65B7245DBC0E0019EBCB /* Info-Mac.plist */, + 0BFB65B8245DBC0E0019EBCB /* Info-TV.plist */, + 0BFB65B9245DBC0E0019EBCB /* Info-Watch.plist */, 1207E0E721AAE15100915CCD /* Interval.swift */, 1207E0E921AAE15700915CCD /* Key.swift */, + 1207E0DF21AAE09500915CCD /* NoteValue.swift */, 1207E0EB21AAE16000915CCD /* Pitch.swift */, - 1207E0ED21AAE16600915CCD /* Chord.swift */, - 1207E0EF21AAE17000915CCD /* Chord Progression.swift */, 1207E0F121AAE17B00915CCD /* Scale.swift */, 1207E0F521AAEF4800915CCD /* ScaleType.swift */, + 1207E0C821AAE05300915CCD /* Swift_Musicology.h */, + 1207E0E321AAE10E00915CCD /* Tempo.swift */, + 1207E0E121AAE0D100915CCD /* TimeSignature.swift */, ); path = "Swift Musicology"; sourceTree = ""; @@ -116,7 +259,6 @@ isa = PBXGroup; children = ( 1207E0D321AAE05300915CCD /* Swift_MusicologyTests.swift */, - 1207E0F321AAE24300915CCD /* MusicTheoryTests.swift */, 1207E0D521AAE05300915CCD /* Info.plist */, ); path = "Swift MusicologyTests"; @@ -125,33 +267,111 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - 1207E0C021AAE05300915CCD /* Headers */ = { + 0BFB65D8245DBD280019EBCB /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0BFB65E1245DBD280019EBCB /* SwiftMusicology_iOS.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0BFB65E5245DBD380019EBCB /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0BFB65EE245DBD380019EBCB /* SwiftMusicology_watchOS.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0BFB65F2245DBD420019EBCB /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0BFB65FB245DBD420019EBCB /* SwiftMusicology_tvOS.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0BFB65FF245DBD500019EBCB /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 1207E0D621AAE05300915CCD /* Swift_Musicology.h in Headers */, + 0BFB6608245DBD500019EBCB /* SwiftMusicology_macOS.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - 1207E0C421AAE05300915CCD /* Swift Musicology */ = { + 0BFB65DC245DBD280019EBCB /* SwiftMusicology iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0BFB65E2245DBD280019EBCB /* Build configuration list for PBXNativeTarget "SwiftMusicology iOS" */; + buildPhases = ( + 0BFB65D8245DBD280019EBCB /* Headers */, + 0BFB65D9245DBD280019EBCB /* Sources */, + 0BFB65DA245DBD280019EBCB /* Frameworks */, + 0BFB65DB245DBD280019EBCB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "SwiftMusicology iOS"; + productName = "SwiftMusicology iOS"; + productReference = 0BFB65DD245DBD280019EBCB /* SwiftMusicology_iOS.framework */; + productType = "com.apple.product-type.framework"; + }; + 0BFB65E9245DBD380019EBCB /* SwiftMusicology watchOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0BFB65EF245DBD380019EBCB /* Build configuration list for PBXNativeTarget "SwiftMusicology watchOS" */; + buildPhases = ( + 0BFB65E5245DBD380019EBCB /* Headers */, + 0BFB65E6245DBD380019EBCB /* Sources */, + 0BFB65E7245DBD380019EBCB /* Frameworks */, + 0BFB65E8245DBD380019EBCB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "SwiftMusicology watchOS"; + productName = "SwiftMusicology watchOS"; + productReference = 0BFB65EA245DBD380019EBCB /* SwiftMusicology_watchOS.framework */; + productType = "com.apple.product-type.framework"; + }; + 0BFB65F6245DBD420019EBCB /* SwiftMusicology tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0BFB65FC245DBD420019EBCB /* Build configuration list for PBXNativeTarget "SwiftMusicology tvOS" */; + buildPhases = ( + 0BFB65F2245DBD420019EBCB /* Headers */, + 0BFB65F3245DBD420019EBCB /* Sources */, + 0BFB65F4245DBD420019EBCB /* Frameworks */, + 0BFB65F5245DBD420019EBCB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "SwiftMusicology tvOS"; + productName = "SwiftMusicology tvOS"; + productReference = 0BFB65F7245DBD420019EBCB /* SwiftMusicology_tvOS.framework */; + productType = "com.apple.product-type.framework"; + }; + 0BFB6603245DBD500019EBCB /* SwiftMusicology macOS */ = { isa = PBXNativeTarget; - buildConfigurationList = 1207E0D921AAE05300915CCD /* Build configuration list for PBXNativeTarget "Swift Musicology" */; + buildConfigurationList = 0BFB6609245DBD500019EBCB /* Build configuration list for PBXNativeTarget "SwiftMusicology macOS" */; buildPhases = ( - 1207E0C021AAE05300915CCD /* Headers */, - 1207E0C121AAE05300915CCD /* Sources */, - 1207E0C221AAE05300915CCD /* Frameworks */, - 1207E0C321AAE05300915CCD /* Resources */, + 0BFB65FF245DBD500019EBCB /* Headers */, + 0BFB6600245DBD500019EBCB /* Sources */, + 0BFB6601245DBD500019EBCB /* Frameworks */, + 0BFB6602245DBD500019EBCB /* Resources */, ); buildRules = ( ); dependencies = ( ); - name = "Swift Musicology"; - productName = "Swift Musicology"; - productReference = 1207E0C521AAE05300915CCD /* Swift_Musicology.framework */; + name = "SwiftMusicology macOS"; + productName = "SwiftMusicology macOS"; + productReference = 0BFB6604245DBD500019EBCB /* SwiftMusicology_macOS.framework */; productType = "com.apple.product-type.framework"; }; 1207E0CD21AAE05300915CCD /* Swift MusicologyTests */ = { @@ -165,7 +385,6 @@ buildRules = ( ); dependencies = ( - 1207E0D121AAE05300915CCD /* PBXTargetDependency */, ); name = "Swift MusicologyTests"; productName = "Swift MusicologyTests"; @@ -182,9 +401,17 @@ LastUpgradeCheck = 1010; ORGANIZATIONNAME = "roux g. buciu"; TargetAttributes = { - 1207E0C421AAE05300915CCD = { - CreatedOnToolsVersion = 10.1; - LastSwiftMigration = 1010; + 0BFB65DC245DBD280019EBCB = { + CreatedOnToolsVersion = 11.4.1; + }; + 0BFB65E9245DBD380019EBCB = { + CreatedOnToolsVersion = 11.4.1; + }; + 0BFB65F6245DBD420019EBCB = { + CreatedOnToolsVersion = 11.4.1; + }; + 0BFB6603245DBD500019EBCB = { + CreatedOnToolsVersion = 11.4.1; }; 1207E0CD21AAE05300915CCD = { CreatedOnToolsVersion = 10.1; @@ -197,23 +424,64 @@ hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = 1207E0BB21AAE05300915CCD; productRefGroup = 1207E0C621AAE05300915CCD /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - 1207E0C421AAE05300915CCD /* Swift Musicology */, + 0BFB65DC245DBD280019EBCB /* SwiftMusicology iOS */, + 0BFB6603245DBD500019EBCB /* SwiftMusicology macOS */, + 0BFB65F6245DBD420019EBCB /* SwiftMusicology tvOS */, + 0BFB65E9245DBD380019EBCB /* SwiftMusicology watchOS */, 1207E0CD21AAE05300915CCD /* Swift MusicologyTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 1207E0C321AAE05300915CCD /* Resources */ = { + 0BFB65DB245DBD280019EBCB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0BFB660C245DBDD10019EBCB /* Info-iOS.plist in Resources */, + 0BFB660D245DBDD10019EBCB /* Info-Mac.plist in Resources */, + 0BFB660E245DBDD10019EBCB /* Info-TV.plist in Resources */, + 0BFB660F245DBDD10019EBCB /* Info-Watch.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0BFB65E8245DBD380019EBCB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0BFB6644245DBE320019EBCB /* Info-iOS.plist in Resources */, + 0BFB6645245DBE320019EBCB /* Info-Mac.plist in Resources */, + 0BFB6646245DBE320019EBCB /* Info-TV.plist in Resources */, + 0BFB6647245DBE320019EBCB /* Info-Watch.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0BFB65F5245DBD420019EBCB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0BFB662A245DBE220019EBCB /* Info-iOS.plist in Resources */, + 0BFB662B245DBE220019EBCB /* Info-Mac.plist in Resources */, + 0BFB662C245DBE220019EBCB /* Info-TV.plist in Resources */, + 0BFB662D245DBE220019EBCB /* Info-Watch.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0BFB6602245DBD500019EBCB /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0BFB6626245DBE1B0019EBCB /* Info-iOS.plist in Resources */, + 0BFB6627245DBE1B0019EBCB /* Info-Mac.plist in Resources */, + 0BFB6628245DBE1B0019EBCB /* Info-TV.plist in Resources */, + 0BFB6629245DBE1B0019EBCB /* Info-Watch.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -227,21 +495,75 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 1207E0C121AAE05300915CCD /* Sources */ = { + 0BFB65D9245DBD280019EBCB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0BFB6610245DBDE40019EBCB /* NoteValue.swift in Sources */, + 0BFB6611245DBDE40019EBCB /* TimeSignature.swift in Sources */, + 0BFB6612245DBDE40019EBCB /* Tempo.swift in Sources */, + 0BFB6613245DBDE40019EBCB /* Accidental.swift in Sources */, + 0BFB6614245DBDE40019EBCB /* Interval.swift in Sources */, + 0BFB6615245DBDE40019EBCB /* Key.swift in Sources */, + 0BFB6616245DBDE40019EBCB /* Pitch.swift in Sources */, + 0BFB6617245DBDE40019EBCB /* Chord.swift in Sources */, + 0BFB6618245DBDE40019EBCB /* ChordProgression.swift in Sources */, + 0BFB6619245DBDE40019EBCB /* Scale.swift in Sources */, + 0BFB661A245DBDE40019EBCB /* ScaleType.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0BFB65E6245DBD380019EBCB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0BFB6639245DBE2D0019EBCB /* NoteValue.swift in Sources */, + 0BFB663A245DBE2D0019EBCB /* TimeSignature.swift in Sources */, + 0BFB663B245DBE2D0019EBCB /* Tempo.swift in Sources */, + 0BFB663C245DBE2D0019EBCB /* Accidental.swift in Sources */, + 0BFB663D245DBE2D0019EBCB /* Interval.swift in Sources */, + 0BFB663E245DBE2D0019EBCB /* Key.swift in Sources */, + 0BFB663F245DBE2D0019EBCB /* Pitch.swift in Sources */, + 0BFB6640245DBE2D0019EBCB /* Chord.swift in Sources */, + 0BFB6641245DBE2D0019EBCB /* ChordProgression.swift in Sources */, + 0BFB6642245DBE2D0019EBCB /* Scale.swift in Sources */, + 0BFB6643245DBE2D0019EBCB /* ScaleType.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0BFB65F3245DBD420019EBCB /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1207E0E821AAE15100915CCD /* Interval.swift in Sources */, - 1207E0EC21AAE16000915CCD /* Pitch.swift in Sources */, - 1207E0F021AAE17000915CCD /* Chord Progression.swift in Sources */, - 1207E0E421AAE10E00915CCD /* Tempo.swift in Sources */, - 1207E0E021AAE09500915CCD /* NoteValue.swift in Sources */, - 1207E0E221AAE0D100915CCD /* TimeSignature.swift in Sources */, - 1207E0E621AAE13E00915CCD /* Accidental.swift in Sources */, - 1207E0F621AAEF4800915CCD /* ScaleType.swift in Sources */, - 1207E0EE21AAE16600915CCD /* Chord.swift in Sources */, - 1207E0F221AAE17B00915CCD /* Scale.swift in Sources */, - 1207E0EA21AAE15700915CCD /* Key.swift in Sources */, + 0BFB662E245DBE270019EBCB /* NoteValue.swift in Sources */, + 0BFB662F245DBE270019EBCB /* TimeSignature.swift in Sources */, + 0BFB6630245DBE270019EBCB /* Tempo.swift in Sources */, + 0BFB6631245DBE270019EBCB /* Accidental.swift in Sources */, + 0BFB6632245DBE270019EBCB /* Interval.swift in Sources */, + 0BFB6633245DBE270019EBCB /* Key.swift in Sources */, + 0BFB6634245DBE270019EBCB /* Pitch.swift in Sources */, + 0BFB6635245DBE270019EBCB /* Chord.swift in Sources */, + 0BFB6636245DBE270019EBCB /* ChordProgression.swift in Sources */, + 0BFB6637245DBE270019EBCB /* Scale.swift in Sources */, + 0BFB6638245DBE270019EBCB /* ScaleType.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0BFB6600245DBD500019EBCB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0BFB661B245DBE060019EBCB /* NoteValue.swift in Sources */, + 0BFB661C245DBE060019EBCB /* TimeSignature.swift in Sources */, + 0BFB661D245DBE060019EBCB /* Tempo.swift in Sources */, + 0BFB661E245DBE060019EBCB /* Accidental.swift in Sources */, + 0BFB661F245DBE060019EBCB /* Interval.swift in Sources */, + 0BFB6620245DBE060019EBCB /* Key.swift in Sources */, + 0BFB6621245DBE060019EBCB /* Pitch.swift in Sources */, + 0BFB6622245DBE060019EBCB /* Chord.swift in Sources */, + 0BFB6623245DBE060019EBCB /* ChordProgression.swift in Sources */, + 0BFB6624245DBE060019EBCB /* Scale.swift in Sources */, + 0BFB6625245DBE060019EBCB /* ScaleType.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -249,22 +571,221 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1207E0F421AAE24300915CCD /* MusicTheoryTests.swift in Sources */, 1207E0D421AAE05300915CCD /* Swift_MusicologyTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 1207E0D121AAE05300915CCD /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 1207E0C421AAE05300915CCD /* Swift Musicology */; - targetProxy = 1207E0D021AAE05300915CCD /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin XCBuildConfiguration section */ + 0BFB65E3245DBD280019EBCB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 8DD4C7KK4P; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "SwiftMusicology iOS/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.acmelabs.SwiftMusicology-iOS"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 0BFB65E4245DBD280019EBCB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 8DD4C7KK4P; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "SwiftMusicology iOS/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.acmelabs.SwiftMusicology-iOS"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 0BFB65F0245DBD380019EBCB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 8DD4C7KK4P; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "SwiftMusicology watchOS/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.acmelabs.SwiftMusicology-watchOS"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 6.2; + }; + name = Debug; + }; + 0BFB65F1245DBD380019EBCB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 8DD4C7KK4P; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "SwiftMusicology watchOS/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.acmelabs.SwiftMusicology-watchOS"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 6.2; + }; + name = Release; + }; + 0BFB65FD245DBD420019EBCB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 8DD4C7KK4P; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "SwiftMusicology tvOS/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.acmelabs.SwiftMusicology-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 13.4; + }; + name = Debug; + }; + 0BFB65FE245DBD420019EBCB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 8DD4C7KK4P; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "SwiftMusicology tvOS/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.acmelabs.SwiftMusicology-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 13.4; + }; + name = Release; + }; + 0BFB660A245DBD500019EBCB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 8DD4C7KK4P; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "SwiftMusicology macOS/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_BUNDLE_IDENTIFIER = "com.acmelabs.SwiftMusicology-macOS"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 0BFB660B245DBD500019EBCB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 8DD4C7KK4P; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "SwiftMusicology macOS/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_BUNDLE_IDENTIFIER = "com.acmelabs.SwiftMusicology-macOS"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; 1207E0D721AAE05300915CCD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -317,11 +838,13 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.1; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; VERSIONING_SYSTEM = "apple-generic"; @@ -375,10 +898,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.1; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; @@ -387,59 +912,6 @@ }; name = Release; }; - 1207E0DA21AAE05300915CCD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Automatic; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 8DD4C7KK4P; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "Swift Musicology/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.acmelabs.Swift-Musicology"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.2; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 1207E0DB21AAE05300915CCD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Automatic; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 8DD4C7KK4P; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "Swift Musicology/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.acmelabs.Swift-Musicology"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; 1207E0DD21AAE05300915CCD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -481,20 +953,47 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 1207E0BF21AAE05300915CCD /* Build configuration list for PBXProject "Swift Musicology" */ = { + 0BFB65E2245DBD280019EBCB /* Build configuration list for PBXNativeTarget "SwiftMusicology iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( - 1207E0D721AAE05300915CCD /* Debug */, - 1207E0D821AAE05300915CCD /* Release */, + 0BFB65E3245DBD280019EBCB /* Debug */, + 0BFB65E4245DBD280019EBCB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0BFB65EF245DBD380019EBCB /* Build configuration list for PBXNativeTarget "SwiftMusicology watchOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0BFB65F0245DBD380019EBCB /* Debug */, + 0BFB65F1245DBD380019EBCB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0BFB65FC245DBD420019EBCB /* Build configuration list for PBXNativeTarget "SwiftMusicology tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0BFB65FD245DBD420019EBCB /* Debug */, + 0BFB65FE245DBD420019EBCB /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 1207E0D921AAE05300915CCD /* Build configuration list for PBXNativeTarget "Swift Musicology" */ = { + 0BFB6609245DBD500019EBCB /* Build configuration list for PBXNativeTarget "SwiftMusicology macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( - 1207E0DA21AAE05300915CCD /* Debug */, - 1207E0DB21AAE05300915CCD /* Release */, + 0BFB660A245DBD500019EBCB /* Debug */, + 0BFB660B245DBD500019EBCB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1207E0BF21AAE05300915CCD /* Build configuration list for PBXProject "Swift Musicology" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1207E0D721AAE05300915CCD /* Debug */, + 1207E0D821AAE05300915CCD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/Swift Musicology/Swift Musicology.xcodeproj/project.xcworkspace/xcuserdata/roux.xcuserdatad/UserInterfaceState.xcuserstate b/Swift Musicology/Swift Musicology.xcodeproj/project.xcworkspace/xcuserdata/roux.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..8f8e403 Binary files /dev/null and b/Swift Musicology/Swift Musicology.xcodeproj/project.xcworkspace/xcuserdata/roux.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Swift Musicology/Swift Musicology.xcodeproj/xcuserdata/roux.xcuserdatad/xcschemes/xcschememanagement.plist b/Swift Musicology/Swift Musicology.xcodeproj/xcuserdata/roux.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..95fb99a --- /dev/null +++ b/Swift Musicology/Swift Musicology.xcodeproj/xcuserdata/roux.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,39 @@ + + + + + SchemeUserState + + Swift Musicology.xcscheme_^#shared#^_ + + orderHint + 0 + + SwiftMusicology iOS.xcscheme_^#shared#^_ + + orderHint + 2 + + SwiftMusicology macOS.xcscheme_^#shared#^_ + + orderHint + 5 + + SwiftMusicology tvOS.xcscheme_^#shared#^_ + + orderHint + 4 + + SwiftMusicology watchOS.xcscheme_^#shared#^_ + + orderHint + 3 + + SwiftMusicology.xcscheme_^#shared#^_ + + orderHint + 1 + + + + diff --git a/Swift Musicology/Swift Musicology/Accidental.swift b/Swift Musicology/Swift Musicology/Accidental.swift index e20c40a..05d0afe 100644 --- a/Swift Musicology/Swift Musicology/Accidental.swift +++ b/Swift Musicology/Swift Musicology/Accidental.swift @@ -8,7 +8,101 @@ import Foundation -/// Used for calculating values of the `Key`s and `Pitche`s. +// MARK: - Accidental Operations + +/// Returns a new accidental by adding up two accidentals in the equation. +/// +/// - Parameters: +/// - lhs: Left hand side of the equation. +/// - 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) +} + +/// Returns a new accidental by substracting two accidentals in the equation. +/// +/// - Parameters: +/// - lhs: Left hand side of the equation. +/// - 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) +} + +/// Returns a new accidental by adding up an int to the accidental in the equation. +/// +/// - Parameters: +/// - lhs: Left hand side of the equation. +/// - 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) +} + +/// Returns a new accidental by substracting an int from the accidental in the equation. +/// +/// - Parameters: +/// - lhs: Left hand side of the equation. +/// - 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) +} + +/// Multiples an accidental with a multiplier. +/// +/// - Parameters: +/// - lhs: Accidental you want to multiply. +/// - rhs: Multiplier. +/// - Returns: Returns a multiplied acceident. +public func * (lhs: Accidental, rhs: Int) -> Accidental { + return Accidental(integerLiteral: lhs.rawValue * rhs) +} + +/// Divides an accidental with a multiplier +/// +/// - Parameters: +/// - lhs: Accidental you want to divide. +/// - rhs: Multiplier. +/// - Returns: Returns a divided accidental. +public func / (lhs: Accidental, rhs: Int) -> Accidental { + return Accidental(integerLiteral: lhs.rawValue / rhs) +} + +/// Checks if the two accidental is identical in terms of their semitone values. +/// +/// - Parameters: +/// - lhs: Left hand side of the equation. +/// - 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 +} + +/// Checks if the two accidental is exactly identical. +/// +/// - Parameters: +/// - lhs: Left hand side of the equation. +/// - 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 + } +} + + +// MARK: - Accidental Definitions + +/// Used for calculating values of the `Key` and `Pitch` objects. public enum Accidental: Codable, Equatable, Hashable { // Base definition of accidentals. @@ -20,15 +114,19 @@ public enum Accidental: Codable, Equatable, Hashable { case sharps(amount: Int) - // Human friendly definition of accidentals. + /// Reduce the `Key` or `Pitch` value one semitone below public static let flat: Accidental = .flats(amount: 1) + /// Increases the `Key` or `Pitch` value one semitone above. public static let sharp: Accidental = .sharps(amount: 1) + /// Reduces the `Key` or `Pitch` value amount two semitones below. public static let doubleFlat: Accidental = .flats(amount: 2) + /// Increases the `Key` or `Pitch` value two semitones 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 to `false` where fonts do not - /// support those symbols. Defaults to `true`. + /// A flag for the `description` function that determines if it should use double + /// sharp and double flat symbols. This is mostly useful for instances where fonts + /// do not support those types of symbols; in that case, set to `false`. + /// Defaults to `true`. public static var shouldUseDoubleFlatAndDoubleSharpNotation = true } @@ -132,95 +230,3 @@ extension Accidental: CustomStringConvertible { } } } - - -// MARK: - Accidental Operations - -/// Returns a new accidental by adding up two accidentals in the equation. -/// -/// - Parameters: -/// - lhs: Left hand side of the equation. -/// - 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) -} - -/// Returns a new accidental by substracting two accidentals in the equation. -/// -/// - Parameters: -/// - lhs: Left hand side of the equation. -/// - 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) -} - -/// Returns a new accidental by adding up an int to the accidental in the equation. -/// -/// - Parameters: -/// - lhs: Left hand side of the equation. -/// - 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) -} - -/// Returns a new accidental by substracting an int from the accidental in the equation. -/// -/// - Parameters: -/// - lhs: Left hand side of the equation. -/// - 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) -} - -/// Multiples an accidental with a multiplier. -/// -/// - Parameters: -/// - lhs: Accidental you want to multiply. -/// - rhs: Multiplier. -/// - Returns: Returns a multiplied acceident. -public func * (lhs: Accidental, rhs: Int) -> Accidental { - return Accidental(integerLiteral: lhs.rawValue * rhs) -} - -/// Divides an accidental with a multiplier -/// -/// - Parameters: -/// - lhs: Accidental you want to divide. -/// - rhs: Multiplier. -/// - Returns: Returns a divided accidental. -public func / (lhs: Accidental, rhs: Int) -> Accidental { - return Accidental(integerLiteral: lhs.rawValue / rhs) -} - -/// Checks if the two accidental is identical in terms of their semitone values. -/// -/// - Parameters: -/// - lhs: Left hand side of the equation. -/// - 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 -} - -/// Checks if the two accidental is exactly identical. -/// -/// - Parameters: -/// - lhs: Left hand side of the equation. -/// - 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 - } -} diff --git a/Swift Musicology/Swift Musicology/Chord.swift b/Swift Musicology/Swift Musicology/Chord.swift index c33a8d3..2dc705e 100644 --- a/Swift Musicology/Swift Musicology/Chord.swift +++ b/Swift Musicology/Swift Musicology/Chord.swift @@ -8,19 +8,19 @@ import Foundation -// MARK: - ChordPart +// MARK: - ChordElement -/// Protocol that defines a printable chord part. +/// Protocol that defines a printable chord element. public protocol ChordDescription: CustomStringConvertible, Codable { /// Notation of chord. var notation: String { get } } -/// Protocol that defines a chord part. -public protocol ChordPart: ChordDescription { +/// Protocol that defines a chord element. +public protocol ChordElement: ChordDescription { /// Interval between the root. var interval: Interval { get } - /// Initilize chord part with interval. + /// Initilize chord element with interval. init?(interval: Interval) } @@ -36,14 +36,14 @@ extension Interval { } } -/// Defines third part of the chord. Second note after the root. -public enum ChordThirdType: Int, ChordPart { - /// Defines major chord. 4 semitones between root. +/// Defines the third note of a chord. +public enum ChordThirdType: Int, ChordElement { + /// Defines major chord. 4 semitones from the root. case major - /// Defines minor chord. 3 semitones between root. + /// Defines minor chord. 3 semitones from the root. case minor - /// Initilize chord part with interval. + /// Initilize chord element with interval. public init?(interval: Interval) { switch interval { case ChordThirdType.major.interval: @@ -55,7 +55,7 @@ public enum ChordThirdType: Int, ChordPart { } } - /// Interval between root. + /// Interval from the root. public var interval: Interval { switch self { case .major: @@ -65,7 +65,7 @@ public enum ChordThirdType: Int, ChordPart { } } - /// Notation of chord part. + /// Notation of chord element. public var notation: String { switch self { case .major: return "" @@ -73,7 +73,7 @@ public enum ChordThirdType: Int, ChordPart { } } - /// Description of chord part. + /// Description of chord element. public var description: String { switch self { case .major: return "Major" @@ -87,16 +87,16 @@ public enum ChordThirdType: Int, ChordPart { } } -/// Defines fifth part of the chord. Third note after root note. -public enum ChordFifthType: Int, ChordPart { - /// Perfect fifth interval between root. +/// Defines the fifth note of a chord. +public enum ChordFifthType: Int, ChordElement { + /// Perfect fifth interval from the root. case perfect /// Half step down of perfect fifth. case diminished /// Half step up of perfect fifth. case agumented - /// Initilize chord part with interval. + /// Initilize chord element with interval. public init?(interval: Interval) { switch interval { case ChordFifthType.perfect.interval: @@ -110,7 +110,7 @@ public enum ChordFifthType: Int, ChordPart { } } - /// Interval between root. + /// Interval from the root. public var interval: Interval { switch self { case .perfect: @@ -122,7 +122,7 @@ public enum ChordFifthType: Int, ChordPart { } } - /// Notation of chord part. + /// Notation of chord element. public var notation: String { switch self { case .perfect: return "" @@ -131,7 +131,7 @@ public enum ChordFifthType: Int, ChordPart { } } - /// Description of chord part. + /// Description of chord element. public var description: String { switch self { case .perfect: return "" @@ -146,12 +146,12 @@ public enum ChordFifthType: Int, ChordPart { } } -/// Defiens sixth chords. If you add the sixth note, you have sixth chord. -public struct ChordSixthType: ChordPart { +/// Defines the sixth note of a chord. +public struct ChordSixthType: ChordElement { /// Default initilizer. public init() {} - /// Initilize chord part with interval. + /// Initilize chord element with interval. public init?(interval: Interval) { switch interval { case .M6: @@ -161,82 +161,89 @@ public struct ChordSixthType: ChordPart { } } - /// Interval between root. + /// Interval from the root. public var interval: Interval { return .M6 } - /// Notation of chord part. + /// Notation of chord element. public var notation: String { return "6" } - /// Description of chord part. + /// Description of chord element. 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 semitones between root. +/// Defines the seventh note of a chord. +public enum ChordSeventhType: Int, ChordElement { + /// Seventh note of the chord. 11 semitones from the root. case major - /// semitone down of seventh note. 10 semitones between root. + /// A semitone down from a seventh note. 10 semitones from the root. case dominant + /// Two semitones down from a seventh note. 9 semitones from the root. + case diminished - /// Initilize chord part with interval. + /// Initilize chord element 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. + /// Interval from the 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. + /// Notation of chord element. public var notation: String { switch self { case .major: return "maj7" case .dominant: return "7" + case .diminished: return "dim7" } } - /// Description of chord part. + /// Description of chord element. 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] + 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 semitones between root. +/// Defines suspended chords. Suspended chords are when the second or fourth scale degrees are played rather than the third. +public enum ChordSuspendedType: Int, ChordElement { + /// Second note of chord instead of third part. 2 semitones from the root. case sus2 - /// Fourth note of chord instead of third part. 5 semitones between root. + /// Fourth note of chord instead of third part. 5 semitones from the root. case sus4 - /// Initilize chord part with interval + /// Initilize chord element with interval public init?(interval: Interval) { switch interval { case ChordSuspendedType.sus2.interval: @@ -248,7 +255,7 @@ public enum ChordSuspendedType: Int, ChordPart { } } - /// Interval between root. + /// Interval from the root. public var interval: Interval { switch self { case .sus2: @@ -258,7 +265,7 @@ public enum ChordSuspendedType: Int, ChordPart { } } - /// Notation of chord part. + /// Notation of chord element. public var notation: String { switch self { case .sus2: return "(sus2)" @@ -266,7 +273,7 @@ public enum ChordSuspendedType: Int, ChordPart { } } - /// Description of chord part. + /// Description of chord element. public var description: String { switch self { case .sus2: return "Suspended 2nd" @@ -280,11 +287,9 @@ public enum ChordSuspendedType: Int, ChordPart { } } -/// 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. +/// Defines chord extensions, or colour tones. If you add one octave up from the second, fourth or sixth notes of the chord, you have extended chords. +public struct ChordExtensionType: ChordElement { + /// Defines types of extended chords. public enum ExtensionType: Int, ChordDescription { /// 9th chord. Second note of the chord, one octave up from root. case ninth = 9 @@ -293,7 +298,7 @@ public struct ChordExtensionType: ChordPart { /// 13th chord. Sixth note of the chord, one octave up from root. case thirteenth = 13 - /// Interval between root. + /// Interval from the root. public var interval: Interval { switch self { case .ninth: @@ -305,7 +310,7 @@ public struct ChordExtensionType: ChordPart { } } - /// Notation of the chord part. + /// Notation of the chord element. public var notation: String { switch self { case .ninth: @@ -317,7 +322,7 @@ public struct ChordExtensionType: ChordPart { } } - /// Description of the chord part. + /// Description of the chord element. public var description: String { switch self { case .ninth: @@ -335,11 +340,11 @@ public struct ChordExtensionType: ChordPart { } } - /// Type of extended chord. + /// Type of extended chord element. public var type: ExtensionType - /// Accident of extended chord. + /// Accidental of extended chord. public var accidental: Accidental - /// If there are no seventh note and only one extended part is this. Defaults false + /// If theri is no seventh note and only one extended part. Defaults to `false`. internal var isAdded: Bool /// Initilizes extended chord. @@ -353,7 +358,7 @@ public struct ChordExtensionType: ChordPart { isAdded = false } - /// Initilize chord part with interval + /// Initilize chord element with interval public init?(interval: Interval) { switch interval.semitones { case ExtensionType.ninth.interval + Accidental.natural: @@ -379,7 +384,7 @@ public struct ChordExtensionType: ChordPart { } } - /// Interval between root. + /// Interval from the root. public var interval: Interval { switch (type, accidental) { case (.ninth, .natural): return .M9 @@ -400,12 +405,12 @@ public struct ChordExtensionType: ChordPart { } } - /// Notation of chord part. + /// Notation of chord element. public var notation: String { return "\(accidental.notation)\(type.notation)" } - /// Description of chord part. + /// Description of chord element. public var description: String { return "\(isAdded ? "Added " : "")\(accidental.description) \(type.description)" } @@ -422,9 +427,34 @@ public struct ChordExtensionType: ChordPart { } } +// MARK - CustomChordType + +/// Create a custom chord with custom intervals. +public struct CustomChordType: ChordDescription { + /// Intervals of the chord type. + public var intervals: [Interval] + + /// Initialize the chord type with custom intervals. + /// + /// - Parameter intervals: Custom intervals. + public init(intervals: [Interval]) { + self.intervals = intervals + } + + /// Notation of the chord type. + public var notation: String { + return description + } + + /// Description of the chord type. + public var description: String { + return "" + } +} + // MARK: - ChordType -/// Checks the equability between two `ChordType`s by their intervals. +/// Checks the equability between two `ChordType` objects by their intervals. /// /// - Parameters: /// - left: Left handside of the equation. @@ -441,7 +471,7 @@ public func == (left: ChordType?, right: ChordType?) -> Bool { } } -/// Defines full type of chord with all chord parts. +/// Defines full type of chord with all chord elements. public struct ChordType: ChordDescription { /// Thirds part. Second note of the chord. public var third: ChordThirdType @@ -469,6 +499,9 @@ public struct ChordType: ChordDescription { } } + /// Describes a custom chord that can not be represent with the current structures. Defaults nil. + public var custom: CustomChordType? + /// Initilze the chord type with its parts. /// /// - Parameters: @@ -477,8 +510,15 @@ public struct ChordType: ChordDescription { /// - 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. - public init(third: ChordThirdType, fifth: ChordFifthType = .perfect, sixth: ChordSixthType? = nil, seventh: ChordSeventhType? = nil, suspended: ChordSuspendedType? = nil, extensions: [ChordExtensionType]? = nil) { + /// - extensions: Extended chords part. Defaults nil. It's possible to add more than one extension to a chord. + /// - custom: Used to fill in with custom intervals 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, + custom: CustomChordType? = nil) { self.third = third self.fifth = fifth self.sixth = sixth @@ -487,14 +527,14 @@ public struct ChordType: ChordDescription { 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)) - } + 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)) + } } } @@ -526,24 +566,21 @@ public struct ChordType: ChordDescription { } } - guard let thirdPart = third, - let fifthPart = fifth - else { return nil } - self = ChordType( - third: thirdPart, - fifth: fifthPart, + third: third ?? .major, + fifth: fifth ?? .perfect, sixth: sixth, seventh: seventh, suspended: suspended, - extensions: extensions + extensions: extensions, + custom: custom ) } - /// Intervals of parts between root. + /// Intervals of parts from the root. public var intervals: [Interval] { - var parts: [ChordPart?] = [sixth == nil ? third : nil, suspended, fifth, sixth, seventh] - parts += extensions?.sorted(by: { $0.type.rawValue < $1.type.rawValue }).map({ $0 as ChordPart? }) ?? [] + var parts: [ChordElement?] = [sixth == nil ? third : nil, suspended, fifth, sixth, seventh] + parts += extensions?.sorted(by: { $0.type.rawValue < $1.type.rawValue }).map({ $0 as ChordElement? }) ?? [] return [.P1] + parts.compactMap({ $0?.interval }) } @@ -733,10 +770,65 @@ public struct Chord: ChordDescription { return "\(key)\(type.notation)\(inversionNotation)" } + /// 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 agumented + if type.fifth == .agumented { + 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 + } + + // MARK: CustomStringConvertible + /// Description of the chord. public var description: String { - let inversionNotation = inversion > 0 ? " \(inversion). Inversion" : "" - return "\(key) \(type)\(inversionNotation)" + let inversionNotation = inversion > 0 ? " \(inversion). Inversion" : "" + return "\(key) \(type)\(inversionNotation)" } } diff --git a/Swift Musicology/Swift Musicology/Chord Progression.swift b/Swift Musicology/Swift Musicology/ChordProgression.swift similarity index 55% rename from Swift Musicology/Swift Musicology/Chord Progression.swift rename to Swift Musicology/Swift Musicology/ChordProgression.swift index bc9ca0f..8c0c143 100644 --- a/Swift Musicology/Swift Musicology/Chord Progression.swift +++ b/Swift Musicology/Swift Musicology/ChordProgression.swift @@ -1,5 +1,5 @@ // -// Chord Progression.swift +// ChordProgression.swift // Swift Musicology // // Created by roux g. buciu on 2018-11-25. @@ -9,11 +9,19 @@ import Foundation /// A struct for storing custom progressions. -public struct CustomChordProgression: Codable { +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 + } + } /// A node of chord progression in intervals. @@ -75,123 +83,75 @@ public enum ChordProgressionNode: Int, CustomStringConvertible, Codable { /// Chord progression enum that you can create hard-coded and custom progressions. public enum ChordProgression: CustomStringConvertible, Codable { /// All nodes from first to seventh. - case allNodes + public static let allNodes = ChordProgression(nodes: [.i, .ii, .iii, .iv, .v, .vi, .vii]) /// I - V - VI - IV progression. - case i_v_vi_iv + public static let i_v_vi_iv = ChordProgression(nodes: [.i, .v, .vi, .iv]) /// VI - V - IV - V progression. - case vi_v_iv_v + public static let vi_v_iv_v = ChordProgression(nodes: [.vi, .v, .iv, .v]) /// I - VI - IV - V progression. - case i_vi_iv_v + public static let i_vi_iv_v = ChordProgression(nodes: [.i, .vi, .iv, .v]) /// I - IV - VI - V progression. - case i_iv_vi_v + public static let i_iv_vi_v = ChordProgression(nodes: [.i, .iv, .vi, .v]) /// I - V - IV - V progression. - case i_v_iv_v + public static let i_v_iv_v = ChordProgression(nodes: [.i, .v, .iv, .v]) /// VI - II - V - I progression. - case vi_ii_v_i + public static let vi_ii_v_i = ChordProgression(nodes: [.vi, .ii, .v, .i]) /// I - VI - II - V progression. - case i_vi_ii_v + public static let i_vi_ii_v = ChordProgression(nodes: [.i, .vi, .ii, .v]) /// I - IV - II - V progression. - case i_iv_ii_v + public static let i_iv_ii_v = ChordProgression(nodes: [.i, .iv, .ii, .v]) /// VI - IV - I - V progression. - case vi_iv_i_v + public static let vi_iv_i_v = ChordProgression(nodes: [.vi, .iv, .i, .v]) /// I - VI - III - VII progression. - case i_vi_iii_vii + public static let i_vi_iii_vii = ChordProgression(nodes: [.i, .vi, .iii, .vii]) /// VI - V - IV - III progression. - case vi_v_iv_iii + public static let vi_v_iv_iii = ChordProgression(nodes: [.vi, .v, .iv, .iii]) /// I - V - VI - III - IV - I - IV - V progression. - case i_v_vi_iii_iv_i_iv_v + 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. - case iv_i_v_iv + public static let iv_i_v_iv = ChordProgression(nodes: [.iv, .i, .v, .iv]) /// I - II - VI - IV progression. - case i_ii_vi_iv + public static let i_ii_vi_iv = ChordProgression(nodes: [.i, .ii, .vi, .iv]) /// I - III - VI - IV progression. - case i_iii_vi_iv + public static let i_iii_vi_iv = ChordProgression(nodes: [.i, .iii, .vi, .iv]) /// I - V - II - IV progression. - case i_v_ii_iv + public static let i_v_ii_iv = ChordProgression(nodes: [.i, .v, .ii, .iv]) /// II - IV - I - V progression. - case ii_iv_i_v - /// Custom progression with any node sequence. - case custom([ChordProgressionNode]) + 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]) { - if let progression = ChordProgression.all.filter({ $0.nodes == nodes }).first { - self = progression - } else { - self = .custom(nodes) - } + self.nodes = nodes } - - /// Returns all nodes of the progression. - public var nodes: [ChordProgressionNode] { - switch self { - case .allNodes: - return [.i, .ii, .iii, .iv, .v, .vi, .vii] - case .i_v_vi_iv: - return [.i, .v, .vi, .iv] - case .vi_v_iv_v: - return [.vi, .v, .iv, .v] - case .i_vi_iv_v: - return [.i, .vi, .iv, .v] - case .i_iv_vi_v: - return [.i, .iv, .vi, .v] - case .i_v_iv_v: - return [.i, .v, .iv, .v] - case .vi_ii_v_i: - return [.v, .ii, .v, .i] - case .i_vi_ii_v: - return [.i, .vi, .ii, .v] - case .i_iv_ii_v: - return [.i, .iv, .ii, .v] - case .vi_iv_i_v: - return [.vi, .iv, .i, .v] - case .i_vi_iii_vii: - return [.i, .vi, .iii, .vii] - case .vi_v_iv_iii: - return [.vi, .v, .vi, .iii] - case .i_v_vi_iii_iv_i_iv_v: - return [.i, .v, .vi, .iii, .iv, .i, .iv, .v] - case .iv_i_v_iv: - return [.iv, .i, .v, .iv] - case .i_ii_vi_iv: - return [.i, .ii, .vi, .iv] - case .i_iii_vi_iv: - return [.i, .iii, .vi, .iv] - case .i_v_ii_iv: - return [.i, .v, .ii, .iv] - case .ii_iv_i_v: - return [.ii, .iv, .i, .v] - case let .custom(nodes): - return 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, - ] + 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: @@ -200,52 +160,58 @@ public enum ChordProgression: CustomStringConvertible, Codable { /// - 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]) - } + 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 + } + return chords } - + // MARK: CustomStringConvertible - + /// Returns the chord progression name. public var description: String { - if case .allNodes = self { - return "All" - } - return nodes.map({ "\($0)" }).joined(separator: " - ") + 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 + 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) + 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) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(nodes, forKey: .nodes) + } + + // MARK: Equatable + + public static func == (lhs: ChordProgression, rhs: ChordProgression) -> Bool { + return lhs.nodes == rhs.nodes } } diff --git a/Swift Musicology/Swift Musicology/Info-Mac.plist b/Swift Musicology/Swift Musicology/Info-Mac.plist new file mode 100644 index 0000000..b7b498e --- /dev/null +++ b/Swift Musicology/Swift Musicology/Info-Mac.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2018 ACME | labs. All rights reserved. + NSPrincipalClass + + + diff --git a/Swift Musicology/Swift Musicology/Info.plist b/Swift Musicology/Swift Musicology/Info-TV.plist similarity index 93% rename from Swift Musicology/Swift Musicology/Info.plist rename to Swift Musicology/Swift Musicology/Info-TV.plist index e1fe4cf..1007fd9 100644 --- a/Swift Musicology/Swift Musicology/Info.plist +++ b/Swift Musicology/Swift Musicology/Info-TV.plist @@ -18,5 +18,7 @@ 1.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + diff --git a/Swift Musicology/Swift Musicology/Info-Watch.plist b/Swift Musicology/Swift Musicology/Info-Watch.plist new file mode 100644 index 0000000..1007fd9 --- /dev/null +++ b/Swift Musicology/Swift Musicology/Info-Watch.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/Swift Musicology/Swift Musicology/Info-iOS.plist b/Swift Musicology/Swift Musicology/Info-iOS.plist new file mode 100644 index 0000000..1007fd9 --- /dev/null +++ b/Swift Musicology/Swift Musicology/Info-iOS.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/Swift Musicology/Swift Musicology/Interval.swift b/Swift Musicology/Swift Musicology/Interval.swift index 4d57b59..9e26850 100644 --- a/Swift Musicology/Swift Musicology/Interval.swift +++ b/Swift Musicology/Swift Musicology/Interval.swift @@ -8,7 +8,32 @@ import Foundation -/// Defines the interval between `Pitch`es in semitones. +// MARK: - Interval Operations + +/// Checks the equality of two `Interval`s in terms of their semitones. +/// +/// - Parameters: +/// - lhs: Left hand side of the equation. +/// - 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 +} + +/// Checks the equality of two `Interval`s in terms of their quality, degree and semitones. +/// +/// - Parameters: +/// - lhs: Left hand side of the equation. +/// - 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 +} + + +// MARK: - Interval Definition + +/// Defines the interval between `Pitch` objects in semitones. public struct Interval: Codable, Equatable { /// Quality type of the interval. @@ -213,26 +238,3 @@ extension Interval: CustomStringConvertible { return "\(quality) \(formattedDegree)" } } - - -// MARK: - Interval Operations - -/// Checks the equality of two `Interval`s in terms of their semitones. -/// -/// - Parameters: -/// - lhs: Left hand side of the equation. -/// - 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 -} - -/// Checks the equality of two `Interval`s in terms of their quality, degree and semitones. -/// -/// - Parameters: -/// - lhs: Left hand side of the equation. -/// - 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 -} diff --git a/Swift Musicology/Swift Musicology/Key.swift b/Swift Musicology/Swift Musicology/Key.swift index 701a85b..44f1bcd 100644 --- a/Swift Musicology/Swift Musicology/Key.swift +++ b/Swift Musicology/Swift Musicology/Key.swift @@ -8,11 +8,42 @@ import Foundation +// MARK: - Key Operations + +/// Checks if two `Key` types are equal in terms of their int values. +/// +/// - Parameters: +/// - lhs: Left hand side of the equation. +/// - 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 +} + +/// Checks if two `Key` types are equal in terms of their type and accidental values. +/// +/// - Parameters: +/// - lhs: Left hand side of the equation. +/// - 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 +} + + +// MARK: - Key Definition + /// Represents the keys that notes and pitches are based on. public struct Key: Codable, Equatable, Hashable { /// 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. + /// parent struct, `Key`. Integer values are based on C = 0 accidentals on western chromatic scale. public enum KeyType: Int, Codable, Equatable, Hashable, ExpressibleByStringLiteral, CustomStringConvertible { case c = 0 case d = 2 @@ -33,7 +64,7 @@ public struct Key: Codable, Equatable, Hashable { /// - 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.index(of: self) + guard let index = KeyType.all.firstIndex(of: self) else { return self } let normalizedDistance = (distance + index) % KeyType.all.count @@ -46,8 +77,8 @@ public struct Key: Codable, Equatable, Hashable { /// - 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.index(of: self), - let targetIndex = KeyType.all.index(of: keyType) + guard let index = KeyType.all.firstIndex(of: self), + let targetIndex = KeyType.all.firstIndex(of: keyType) else { return 0 } return targetIndex - index } @@ -180,14 +211,14 @@ extension Key: ExpressibleByStringLiteral { 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])) + 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) @@ -202,32 +233,3 @@ extension Key: CustomStringConvertible { return "\(type)\(accidental)" } } - - -// MARK: - Key Operations - -/// Checks if two `Key` types are equal in terms of their int values. -/// -/// - Parameters: -/// - lhs: Left hand side of the equation. -/// - 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 -} - -/// Checks if two `Key` types are equal in terms of their type and accidental values. -/// -/// - Parameters: -/// - lhs: Left hand side of the equation. -/// - 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 -} diff --git a/Swift Musicology/Swift Musicology/NoteValue.swift b/Swift Musicology/Swift Musicology/NoteValue.swift index 8abf61e..516e05c 100644 --- a/Swift Musicology/Swift Musicology/NoteValue.swift +++ b/Swift Musicology/Swift Musicology/NoteValue.swift @@ -13,7 +13,8 @@ import Foundation /// Defines the different types of note values that are generally available in music notation, /// from whole notes to sixtyfourth notes. -public enum NoteValueType: Double, Codable { +public enum NoteValueType: Double, Codable, CaseIterable { + case doubleWhole = 0.5 case whole = 1 case half = 2 case quarter = 4 @@ -39,7 +40,20 @@ public enum NoteModifier: Double, Codable { } -// MARK: - NoteValue +// MARK: - NoteValue Operations + +/// Calculates how many notes of a single `NoteValueType` is equivalent to a given `NoteValue`. +/// +/// - Parameters: +/// - noteValue: The note value to be measured. +/// - 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.modifier.rawValue * noteValueType.rawValue / noteValue.type.rawValue +} + + +// MARK: - NoteValue Definition /// Used to denfine the duration of a note. public struct NoteValue: Codable { @@ -60,16 +74,3 @@ public struct NoteValue: Codable { self.modifier = modifier } } - - -// MARK: - NoteValue Operations - -/// Calculates how many notes of a single `NoteValueType` is equivalent to a given `NoteValue`. -/// -/// - Parameters: -/// - noteValue: The note value to be measured. -/// - 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.modifier.rawValue * noteValueType.rawValue / noteValue.type.rawValue -} diff --git a/Swift Musicology/Swift Musicology/Pitch.swift b/Swift Musicology/Swift Musicology/Pitch.swift index 041a613..75aa56c 100644 --- a/Swift Musicology/Swift Musicology/Pitch.swift +++ b/Swift Musicology/Swift Musicology/Pitch.swift @@ -193,8 +193,8 @@ public func - (lhs: Pitch, rhs: Pitch) -> Interval { let bottom = min(lhs, rhs) let diff = top.rawValue - bottom.rawValue - let bottomKeyIndex = Key.KeyType.all.index(of: bottom.key.type) ?? 0 - let topKeyIndex = Key.KeyType.all.index(of: top.key.type) ?? 0 + 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) @@ -268,7 +268,7 @@ public func === (left: Pitch, right: Pitch) -> Bool { return left.key == right.key && left.octave == right.octave } -/// Compares two `Pitch`es in terms of their semitones. +/// Compares two `Pitch` objects in terms of their semitones. /// /// - Parameters: /// - lhs: Left hand side of the equation. diff --git a/Swift Musicology/Swift Musicology/Scale.swift b/Swift Musicology/Swift Musicology/Scale.swift index f3b512b..ffec51f 100644 --- a/Swift Musicology/Swift Musicology/Scale.swift +++ b/Swift Musicology/Swift Musicology/Scale.swift @@ -8,122 +8,137 @@ import Foundation +// MARK: - Scale Operations + +/// Checks the equability between two `Scale`s by their base key and notes. +/// +/// - Parameters: +/// - left: Left handside of the equation. +/// - 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 +} + + +// MARK: - Scale Definition + /// Scale object with `ScaleType` and scale's key of `NoteType`. /// Could calculate note sequences in [Pitch] format. public struct Scale: Equatable, 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) - } + /// Type of the scale that has `interval` info. + public var type: ScaleType - /// 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 - } + /// 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 + } } 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) - } - } - - return chords + /// 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 + } } extension Scale: CustomStringConvertible { @@ -132,16 +147,3 @@ extension Scale: CustomStringConvertible { return "\(key) \(type): " + keys.map({ "\($0)" }).joined(separator: ", ") } } - - -// MARK: - Scale Operations - -/// Checks the equability between two `Scale`s by their base key and notes. -/// -/// - Parameters: -/// - left: Left handside of the equation. -/// - 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 -} diff --git a/Swift Musicology/Swift Musicology/ScaleType.swift b/Swift Musicology/Swift Musicology/ScaleType.swift index 3ad0811..d31fb95 100644 --- a/Swift Musicology/Swift Musicology/ScaleType.swift +++ b/Swift Musicology/Swift Musicology/ScaleType.swift @@ -9,272 +9,210 @@ import Foundation /// Represents scale by the intervals between note sequences. -public enum ScaleType: Equatable { - case major - case minor - case harmonicMinor - case melodicMinor - case pentatonicMajor - case pentatonicMinor - case pentatonicBlues - case pentatonicNeutral - case ionian - case aeolian - case dorian - case mixolydian - case phrygian - case lydian - case locrian - case dimHalf - case dimWhole - case whole - case augmented - case chromatic - case roumanianMinor - case spanishGypsy - case blues - case diatonic - case doubleHarmonic - case eightToneSpanish - case enigmatic - case leadingWholeTone - case lydianAugmented - case neopolitanMajor - case neopolitanMinor - case pelog - case prometheus - case prometheusNeopolitan - case sixToneSymmetrical - case superLocrian - case lydianMinor - case lydianDiminished - case nineToneScale - case auxiliaryDiminished - case auxiliaryAugmented - case auxiliaryDimBlues - case majorLocrian - case overtone - case diminishedWholeTone - case pureMinor - case dominant7th - - /// Custom scale with given interval set. - case custom(intervals: [Interval], description: String) - - /// Tries to initilize scale with a matching interval series. If no scale matched - /// with intervals, than initlizes custom scale. - /// - /// - Parameters: - /// - intervals: Intervals of the chord. - /// - description: In case of .custom type scale, you probably need description. - public init(intervals: [Interval], description: String = "") { - if let scale = ScaleType.all.filter({ $0.intervals == intervals }).first { - self = scale - } else { - self = .custom(intervals: intervals, description: description) - } - } - - /// Intervals of the different scales. - public var intervals: [Interval] { - switch self { - case .major: return [.P1, .M2, .M3, .P4, .P5, .M6, .M7] - case .minor: return [.P1, .M2, .m3, .P4, .P5, .m6, .m7] - case .harmonicMinor: return [.P1, .M2, .m3, .P4, .P5, .m6, .M7] - case .dorian: return [.P1, .M2, .m3, .P4, .P5, .M6, .m7] - case .phrygian: return [.P1, .m2, .m3, .P4, .P5, .m6, .m7] - case .lydian: return [.P1, .M2, .M3, .d5, .P5, .M6, .M7] - case .mixolydian: return [.P1, .M2, .M3, .P4, .P5, .M6, .m7] - case .locrian: return [.P1, .m2, .m3, .P4, .d5, .m6, .m7] - case .melodicMinor: return [.P1, .M2, .m3, .P4, .P5, .M6, .M7] - case .pentatonicMajor: return [.P1, .M2, .M3, .P5, .M6] - case .pentatonicMinor: return [.P1, .m3, .P4, .P5, .m7] - case .pentatonicBlues: return [.P1, .m3, .P4, .d5, .P5, .m7] - case .pentatonicNeutral: return [.P1, .M2, .P4, .P5, .m7] - case .ionian: return [.P1, .M2, .M3, .P4, .P5, .M6, .M7] - case .aeolian: return [.P1, .M2, .m3, .P4, .P5, .m6, .m7] - case .dimHalf: return [.P1, .m2, .m3, .M3, .d5, .P5, .M6, .m7] - case .dimWhole: return [.P1, .M2, .m3, .P4, .d5, .m6, .M6, .M7] - case .whole: return [.P1, .M2, .M3, .d5, .m6, .m7] - case .augmented: return [.m3, .M3, .P5, .m6, .M7] - case .chromatic: return [.P1, .m2, .M2, .m3, .M3, .P4, .d5, .P5, .m6, .M6, .m7, .M7] - case .roumanianMinor: return [.P1, .M2, .m3, .d5, .P5, .M6, .m7] - case .spanishGypsy: return [.P1, .m2, .M3, .P4, .P5, .m6, .m7] - case .blues: return [.P1, .m3, .P4, .d5, .P5, .m7] - case .diatonic: return [.P1, .M2, .M3, .P5, .M6] - case .doubleHarmonic: return [.P1, .m2, .M3, .P4, .P5, .m6, .M7] - case .eightToneSpanish: return [.P1, .m2, .m3, .M3, .P4, .d5, .m6, .m7] - case .enigmatic: return [.P1, .m2, .M3, .d5, .m6, .m7, .M7] - case .leadingWholeTone: return [.P1, .M2, .M3, .d5, .m6, .M6, .m7] - case .lydianAugmented: return [.P1, .M2, .M3, .d5, .m6, .M6, .M7] - case .neopolitanMajor: return [.P1, .m2, .m3, .P4, .P5, .M6, .M7] - case .neopolitanMinor: return [.P1, .m2, .m3, .P4, .P5, .m6, .m7] - case .pelog: return [.P1, .m2, .m3, .d5, .m7, .M7] - case .prometheus: return [.P1, .M2, .M3, .d5, .M6, .m7] - case .prometheusNeopolitan: return [.P1, .m2, .M3, .d5, .M6, .m7] - case .sixToneSymmetrical: return [.P1, .m2, .M3, .P4, .m6, .M6] - case .superLocrian: return [.P1, .m2, .m3, .M3, .d5, .m6, .m7] - case .lydianMinor: return [.P1, .M2, .M3, .d5, .P5, .m6, .m7] - case .lydianDiminished: return [.P1, .M2, .m3, .d5, .P5, .m6, .m7] - case .nineToneScale: return [.P1, .M2, .m3, .M3, .d5, .P5, .m6, .M6, .M7] - case .auxiliaryDiminished: return [.P1, .M2, .m3, .P4, .d5, .m6, .M6, .M7] - case .auxiliaryAugmented: return [.P1, .M2, .M3, .d5, .m6, .m7] - case .auxiliaryDimBlues: return [.P1, .m2, .m3, .M3, .d5, .P5, .M6, .m7] - case .majorLocrian: return [.P1, .M2, .M3, .P4, .d5, .m6, .m7] - case .overtone: return [.P1, .M2, .M3, .d5, .P5, .M6, .m7] - case .diminishedWholeTone: return [.P1, .m2, .m3, .M3, .d5, .m6, .m7] - case .pureMinor: return [.P1, .M2, .m3, .P4, .P5, .m6, .m7] - case .dominant7th: return [.P1, .M2, .M3, .P4, .P5, .M6, .m7] - case .custom(let intervals, _): return intervals - } - } - - /// An array of all `ScaleType` values. - public static var all: [ScaleType] { - return [ - .major, - .minor, - .harmonicMinor, - .melodicMinor, - .pentatonicMajor, - .pentatonicMinor, - .pentatonicBlues, - .pentatonicNeutral, - .ionian, - .aeolian, - .dorian, - .mixolydian, - .phrygian, - .lydian, - .locrian, - .dimHalf, - .dimWhole, - .whole, - .augmented, - .chromatic, - .roumanianMinor, - .spanishGypsy, - .blues, - .diatonic, - .doubleHarmonic, - .eightToneSpanish, - .enigmatic, - .leadingWholeTone, - .lydianAugmented, - .neopolitanMajor, - .neopolitanMinor, - .pelog, - .prometheus, - .prometheusNeopolitan, - .sixToneSymmetrical, - .superLocrian, - .lydianMinor, - .lydianDiminished, - .nineToneScale, - .auxiliaryDiminished, - .auxiliaryAugmented, - .auxiliaryDimBlues, - .majorLocrian, - .overtone, - .diminishedWholeTone, - .pureMinor, - .dominant7th, - ] - } -} +public struct ScaleType: CustomStringConvertible { + /// 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 dimHalf = ScaleType(intervals: [.P1, .m2, .m3, .M3, .d5, .P5, .M6, .m7], description: "Half Diminished") + /// Whole diminished scale. + public static let dimWhole = 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") + /// Blues scale. + public static let blues = ScaleType(intervals: [.P1, .m3, .P4, .d5, .P5, .m7], description: "Blues") + /// 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, .d5, .m6, .m7, .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, .d5, .m6, .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, .d5, .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 nineToneScale = ScaleType(intervals: [.P1, .M2, .m3, .M3, .d5, .P5, .m6, .M6, .M7], description: "Nine Tone Scale") + /// 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") + /// Pure minor scale. + public static let pureMinor = ScaleType(intervals: [.P1, .M2, .m3, .P4, .P5, .m6, .m7], description: "Pure Minor") + /// Dominant seventh scale. + public static let dominant7th = ScaleType(intervals: [.P1, .M2, .M3, .P4, .P5, .M6, .m7], description: "Dominant 7th") -extension ScaleType: Codable { - /// Keys that conforms CodingKeys protocol to map properties. - private enum CodingKeys: String, CodingKey { - /// semitone property of `Interval`. - case intervals - } - - /// 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) - self = ScaleType(intervals: intervals) - } - - /// 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) - } + /// 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 + } + + /// An array of all `ScaleType` values. + public static var all: [ScaleType] { + return [ + .major, + .minor, + .harmonicMinor, + .melodicMinor, + .pentatonicMajor, + .pentatonicMinor, + .pentatonicBlues, + .pentatonicNeutral, + .ionian, + .aeolian, + .dorian, + .mixolydian, + .phrygian, + .lydian, + .locrian, + .dimHalf, + .dimWhole, + .whole, + .augmented, + .chromatic, + .romanianMinor, + .spanishGypsy, + .blues, + .diatonic, + .doubleHarmonic, + .eightToneSpanish, + .enigmatic, + .leadingWholeTone, + .lydianAugmented, + .neopolitanMajor, + .neopolitanMinor, + .pelog, + .prometheus, + .prometheusNeopolitan, + .sixToneSymmetrical, + .superLocrian, + .lydianMinor, + .lydianDiminished, + .nineToneScale, + .auxiliaryDiminished, + .auxiliaryAugmented, + .auxiliaryDimBlues, + .majorLocrian, + .overtone, + .diminishedWholeTone, + .pureMinor, + .dominant7th, + ] + } } -extension ScaleType: CustomStringConvertible { - /// Converts `ScaleType` to string with its name. - public var description: String { - switch self { - case .major: return "Major" - case .minor: return "Minor" - case .harmonicMinor: return "Harmonic Minor" - case .melodicMinor: return "Melodic Minor" - case .pentatonicMajor: return "Pentatonic Major" - case .pentatonicMinor: return "Pentatonic Minor" - case .pentatonicBlues: return "Pentatonic Blues" - case .pentatonicNeutral: return "Pentatonic Neutral" - case .ionian: return "Ionian" - case .aeolian: return "Aeolian" - case .dorian: return "Dorian" - case .mixolydian: return "Mixolydian" - case .phrygian: return "Phrygian" - case .lydian: return "Lydian" - case .locrian: return "Locrian" - case .dimHalf: return "Half Diminished" - case .dimWhole: return "Whole Diminished" - case .whole: return "Whole" - case .augmented: return "Augmented" - case .chromatic: return "Chromatic" - case .roumanianMinor: return "Roumanian Minor" - case .spanishGypsy: return "Spanish Gypsy" - case .blues: return "Blues" - case .diatonic: return "Diatonic" - case .doubleHarmonic: return "Double Harmonic" - case .eightToneSpanish: return "Eight Tone Spanish" - case .enigmatic: return "Enigmatic" - case .leadingWholeTone: return "Leading Whole Tone" - case .lydianAugmented: return "Lydian Augmented" - case .neopolitanMajor: return "Neopolitan Major" - case .neopolitanMinor: return "Neopolitan Minor" - case .pelog: return "Pelog" - case .prometheus: return "Prometheus" - case .prometheusNeopolitan: return "Prometheus Neopolitan" - case .sixToneSymmetrical: return "Six Tone Symmetrical" - case .superLocrian: return "Super Locrian" - case .lydianMinor: return "Lydian Minor" - case .lydianDiminished: return "Lydian Diminished" - case .nineToneScale: return "Nine Tone Scale" - case .auxiliaryDiminished: return "Auxiliary Diminished" - case .auxiliaryAugmented: return "Auxiliary Augmented" - case .auxiliaryDimBlues: return "Auxiliary Diminished Blues" - case .majorLocrian: return "Major Locrian" - case .overtone: return "Overtone" - case .diminishedWholeTone: return "Diminished Whole Tone" - case .pureMinor: return "Pure Minor" - case .dominant7th: return "Dominant 7th" - case let .custom(_, description): return description - } +extension ScaleType: 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 } } +extension ScaleType: 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 + } -// MARK: - ScaleType Operations + /// 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) + } -/// 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 func == (left: ScaleType, right: ScaleType) -> Bool { - return left.intervals == right.intervals + /// 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) + } } diff --git a/Swift Musicology/Swift Musicology/Tempo.swift b/Swift Musicology/Swift Musicology/Tempo.swift index 6b34a56..4f99d22 100644 --- a/Swift Musicology/Swift Musicology/Tempo.swift +++ b/Swift Musicology/Swift Musicology/Tempo.swift @@ -11,16 +11,48 @@ import Foundation /// Defines the tempo with beats per second and a time signature. public struct Tempo: Codable { + /// Time signature of music. public var timeSignature: TimeSignature - public var bpm: Double - /// Initilizes tempo with time signature and BPM, with a default of 4/4 at 120 BPM. + /// Beats per minute. + public var bpm: Double + + /// Initilizes tempo with a time signature and a BPM. /// /// - Parameters: - /// - timeSignature: A TimeSignature object. + /// - timeSignature: Time Signature. /// - bpm: Beats per minute. public init(timeSignature: TimeSignature = TimeSignature(), bpm: Double = 120.0) { - self.timeSignature = timeSignature - self.bpm = bpm + 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.rawValue / noteValue.type.rawValue) * 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 * ((4 / noteValue.type.rawValue) * 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) + } +} + +extension Tempo: CustomStringConvertible { + + public var description: String { + return "\(bpm)" } } diff --git a/Swift Musicology/Swift Musicology/TimeSignature.swift b/Swift Musicology/Swift Musicology/TimeSignature.swift index b993cde..eaec872 100644 --- a/Swift Musicology/Swift Musicology/TimeSignature.swift +++ b/Swift Musicology/Swift Musicology/TimeSignature.swift @@ -44,3 +44,9 @@ public struct TimeSignature: Codable { } } +extension TimeSignature: CustomStringConvertible { + + public var description: String { + return "\(beats)/\(Int(noteValue.rawValue))" + } +} diff --git a/Swift Musicology/Swift MusicologyTests/MusicTheoryTests.swift b/Swift Musicology/Swift MusicologyTests/MusicTheoryTests.swift deleted file mode 100644 index 0ba2679..0000000 --- a/Swift Musicology/Swift MusicologyTests/MusicTheoryTests.swift +++ /dev/null @@ -1,351 +0,0 @@ -//// -//// MusicTheoryTests.swift -//// Swift MusicologyTests -//// -//// Created by roux g. buciu on 2018-11-25. -//// Copyright © 2018 roux g. buciu. All rights reserved. -//// -// -//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() -// } -//} -// -//// 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, isPreferredAccidentalSharps: 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 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() { -// let noteValue = NoteValue(type: .half, modifier: .dotted) -// XCTAssertEqual(noteValue / NoteValueType.sixteenth, 12) -// XCTAssertEqual(noteValue / NoteValueType.whole, 0.75) -// } -// -// 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) -// } -//} -// -//// 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) -// } -//} -// -//// 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), -// ] -// ), -// 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 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 -// } -// } -// return true -//} diff --git a/Swift Musicology/SwiftMusicology iOS/Info.plist b/Swift Musicology/SwiftMusicology iOS/Info.plist new file mode 100644 index 0000000..9bcb244 --- /dev/null +++ b/Swift Musicology/SwiftMusicology iOS/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/Swift Musicology/SwiftMusicology iOS/SwiftMusicology_iOS.h b/Swift Musicology/SwiftMusicology iOS/SwiftMusicology_iOS.h new file mode 100644 index 0000000..ea82de1 --- /dev/null +++ b/Swift Musicology/SwiftMusicology iOS/SwiftMusicology_iOS.h @@ -0,0 +1,19 @@ +// +// SwiftMusicology_iOS.h +// SwiftMusicology iOS +// +// Created by roux g. buciu on 2020-05-02. +// Copyright © 2020 roux g. buciu. All rights reserved. +// + +#import + +//! Project version number for SwiftMusicology_iOS. +FOUNDATION_EXPORT double SwiftMusicology_iOSVersionNumber; + +//! Project version string for SwiftMusicology_iOS. +FOUNDATION_EXPORT const unsigned char SwiftMusicology_iOSVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Swift Musicology/SwiftMusicology macOS/Info.plist b/Swift Musicology/SwiftMusicology macOS/Info.plist new file mode 100644 index 0000000..efaca6b --- /dev/null +++ b/Swift Musicology/SwiftMusicology macOS/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2020 roux g. buciu. All rights reserved. + + diff --git a/Swift Musicology/SwiftMusicology macOS/SwiftMusicology_macOS.h b/Swift Musicology/SwiftMusicology macOS/SwiftMusicology_macOS.h new file mode 100644 index 0000000..69d9813 --- /dev/null +++ b/Swift Musicology/SwiftMusicology macOS/SwiftMusicology_macOS.h @@ -0,0 +1,19 @@ +// +// SwiftMusicology_macOS.h +// SwiftMusicology macOS +// +// Created by roux g. buciu on 2020-05-02. +// Copyright © 2020 roux g. buciu. All rights reserved. +// + +#import + +//! Project version number for SwiftMusicology_macOS. +FOUNDATION_EXPORT double SwiftMusicology_macOSVersionNumber; + +//! Project version string for SwiftMusicology_macOS. +FOUNDATION_EXPORT const unsigned char SwiftMusicology_macOSVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Swift Musicology/SwiftMusicology tvOS/Info.plist b/Swift Musicology/SwiftMusicology tvOS/Info.plist new file mode 100644 index 0000000..9bcb244 --- /dev/null +++ b/Swift Musicology/SwiftMusicology tvOS/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/Swift Musicology/SwiftMusicology tvOS/SwiftMusicology_tvOS.h b/Swift Musicology/SwiftMusicology tvOS/SwiftMusicology_tvOS.h new file mode 100644 index 0000000..53850ab --- /dev/null +++ b/Swift Musicology/SwiftMusicology tvOS/SwiftMusicology_tvOS.h @@ -0,0 +1,19 @@ +// +// SwiftMusicology_tvOS.h +// SwiftMusicology tvOS +// +// Created by roux g. buciu on 2020-05-02. +// Copyright © 2020 roux g. buciu. All rights reserved. +// + +#import + +//! Project version number for SwiftMusicology_tvOS. +FOUNDATION_EXPORT double SwiftMusicology_tvOSVersionNumber; + +//! Project version string for SwiftMusicology_tvOS. +FOUNDATION_EXPORT const unsigned char SwiftMusicology_tvOSVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Swift Musicology/SwiftMusicology watchOS/Info.plist b/Swift Musicology/SwiftMusicology watchOS/Info.plist new file mode 100644 index 0000000..9bcb244 --- /dev/null +++ b/Swift Musicology/SwiftMusicology watchOS/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/Swift Musicology/SwiftMusicology watchOS/SwiftMusicology_watchOS.h b/Swift Musicology/SwiftMusicology watchOS/SwiftMusicology_watchOS.h new file mode 100644 index 0000000..88061d8 --- /dev/null +++ b/Swift Musicology/SwiftMusicology watchOS/SwiftMusicology_watchOS.h @@ -0,0 +1,19 @@ +// +// SwiftMusicology_watchOS.h +// SwiftMusicology watchOS +// +// Created by roux g. buciu on 2020-05-02. +// Copyright © 2020 roux g. buciu. All rights reserved. +// + +#import + +//! Project version number for SwiftMusicology_watchOS. +FOUNDATION_EXPORT double SwiftMusicology_watchOSVersionNumber; + +//! Project version string for SwiftMusicology_watchOS. +FOUNDATION_EXPORT const unsigned char SwiftMusicology_watchOSVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Swift Musicology/SwiftMusicology/Info.plist b/Swift Musicology/SwiftMusicology/Info.plist new file mode 100644 index 0000000..9bcb244 --- /dev/null +++ b/Swift Musicology/SwiftMusicology/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/Swift Musicology/SwiftMusicology/SwiftMusicology.h b/Swift Musicology/SwiftMusicology/SwiftMusicology.h new file mode 100644 index 0000000..118f296 --- /dev/null +++ b/Swift Musicology/SwiftMusicology/SwiftMusicology.h @@ -0,0 +1,19 @@ +// +// SwiftMusicology.h +// SwiftMusicology +// +// Created by roux g. buciu on 2020-05-02. +// Copyright © 2020 roux g. buciu. All rights reserved. +// + +#import + +//! Project version number for SwiftMusicology. +FOUNDATION_EXPORT double SwiftMusicologyVersionNumber; + +//! Project version string for SwiftMusicology. +FOUNDATION_EXPORT const unsigned char SwiftMusicologyVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +