Skip to content

Commit

Permalink
make core objects codable (#27)
Browse files Browse the repository at this point in the history
* proper codable support

* codable cleanup

* added documentation to scale implementation
  • Loading branch information
SebastianBoldt authored Jan 28, 2024
1 parent 27e8c54 commit 1103db6
Show file tree
Hide file tree
Showing 11 changed files with 34 additions and 11 deletions.
2 changes: 1 addition & 1 deletion Sources/Tonic/Accidental.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Foundation
/// A way to describe modification to a ``Note`` or ``NoteClass``
///
/// A semitone offset applied to a note that does not change the letter of the note, just the pitch.
public enum Accidental: Int8, CaseIterable, Equatable, Hashable {
public enum Accidental: Int8, CaseIterable, Equatable, Hashable, Codable {
static var count: Int { Accidental.allCases.count }
static var naturalIndex: Int { count / 2 }

Expand Down
4 changes: 2 additions & 2 deletions Sources/Tonic/BitSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import Foundation

/// Interface to bit sets used to represent sets of pitches and sets of notes.
public protocol BitSet: Hashable {
public protocol BitSet: Hashable, Codable {
init()
func isSet(bit: Int) -> Bool
mutating func add(bit: Int)
Expand Down Expand Up @@ -195,7 +195,7 @@ public protocol IntRepresentable {
var intValue: Int { get }
}

public struct BitSetAdapter<T: IntRepresentable, B: BitSet>: Hashable {
public struct BitSetAdapter<T: IntRepresentable, B: BitSet>: Hashable, Codable {
public var bits: B

public init() {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Tonic/Chord.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Foundation
///
/// A representation of a chord as a set of note classes, with a root note class,
/// and an inversion defined by the lowest note in the chord.
public struct Chord: Equatable {
public struct Chord: Equatable, Codable {
/// Root note class of the chord
public let root: NoteClass

Expand Down
3 changes: 2 additions & 1 deletion Sources/Tonic/ChordType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import Foundation

/// Chord type as defined by a set of intervals from a root note class
public enum ChordType: String, CaseIterable {
public enum ChordType: String, CaseIterable, Codable {

/// Major Triad: Major Third, Perfect Fifth
case majorTriad

Expand Down
2 changes: 1 addition & 1 deletion Sources/Tonic/Interval.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Foundation
/// An interval distance is measured in degree (number of letters away) and
/// quality of the interval (essentialy number of semitones away).
/// Some Intervals refer to the same difference in pitch.
public enum Interval: Int, CaseIterable {
public enum Interval: Int, CaseIterable, Codable {
/// Perfect Unison
case P1

Expand Down
2 changes: 1 addition & 1 deletion Sources/Tonic/Key.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Foundation
/// The key is the set of notes that are played in a composition, or portion of a composition.
///
/// A key is composed of a Root ``Note``, and a ``Scale``.
public struct Key: Equatable {
public struct Key: Equatable, Codable {
/// The primary note class of the key, also known as the tonic
public let root: NoteClass

Expand Down
2 changes: 1 addition & 1 deletion Sources/Tonic/Letter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Foundation
///
/// These letters can be modified by adding an ``Accidental`` to describe any ``NoteClass``.
/// And by specificying an octave, you can create any ``Note``.
public enum Letter: Int, CaseIterable, Equatable, Hashable {
public enum Letter: Int, CaseIterable, Equatable, Hashable, Codable {
case C, D, E, F, G, A, B

var baseNote: UInt8 {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Tonic/Note.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import Foundation

/// A pitch with a particular spelling.
public struct Note: Equatable, Hashable {
public struct Note: Equatable, Hashable, Codable {
/// Base name for the note
public var noteClass: NoteClass = .init(.C, accidental: .natural)

Expand Down
2 changes: 1 addition & 1 deletion Sources/Tonic/NoteClass.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Foundation
public typealias NoteClassSet = BitSetAdapter<NoteClass, BitSet64>

/// A note letter and accidental which spell a note. This leaves out the octave of the note.
public struct NoteClass: Equatable, Hashable {
public struct NoteClass: Equatable, Hashable, Codable {
/// Letter of the note class
public var letter: Letter

Expand Down
2 changes: 1 addition & 1 deletion Sources/Tonic/Pitch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public extension PitchSet {
///
/// We want to use a notion of pitch that lends itself to combinatorial algorithms,
/// as opposed to using e.g. a fundamental frequency.
public struct Pitch: Equatable, Hashable {
public struct Pitch: Equatable, Hashable, Codable {
/// MIDI Note Number 0-127
public var midiNoteNumber: Int8

Expand Down
22 changes: 22 additions & 0 deletions Sources/Tonic/Scale.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,25 @@ public struct Scale: OptionSet, Hashable {
rawValue = r
}
}

/// Automatic synthesis of Codable would use OptionSets RawRepresentable Conformance to de- and encode objects.
/// Unfortunatly this will lead to the loss of the "description" property. That's why we decided to create explicit codable support.
extension Scale: Codable {
private enum CodingKeys: String, CodingKey {
case intervals
case description
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let intervals = try container.decode([Interval].self, forKey: .intervals)
let description = try container.decode(String.self, forKey: .description)
self = .init(intervals: intervals, description: description)
}

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

0 comments on commit 1103db6

Please sign in to comment.