Skip to content

Commit

Permalink
Merge branch 'latest' of https://github.com/dn-m/MusicXML into latest
Browse files Browse the repository at this point in the history
  • Loading branch information
jsbean committed Oct 21, 2019
2 parents 66713b1 + 238138b commit 67feb23
Show file tree
Hide file tree
Showing 14 changed files with 85 additions and 180 deletions.
68 changes: 11 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ When rendered graphically, this score example should look something like this:

![Hello, world!](Documentation/hello_world.gif)

### musicXML Representation
### XML Representation

The musicXML representation looks like this:

Expand Down Expand Up @@ -63,101 +63,55 @@ The musicXML representation looks like this:
</score-partwise>
```

### Constructing the "Hello, world!" example using the `MusicXML` library
### `MusicXML` Representation

Let's build up this musical example from the ground up, using the `MusicXML` library.

First, we will create our whole note:
To construct the "Hello, world!" example in Swift looks like this:

```Swift
let note = Note(
pitch: Pitch(step: .c, octave: 4),
duration: 4,
type: .whole
)
```

Let's establish our `Key`, `Time`, and `Clef`:

```Swift
let key = Key(fifths: 0)
let time = Time(4,4)
let clef = Clef(sign: .g, line: 2)
```

We can bundle these attributes up:

```Swift
let attributes = Attributes(
divisions: 1,
keys: [key],
times: [time],
clefs: [clef]
)
```

Now, we have all the information we need to put together our single measure. In this case, we are traversing the score in a partwise fashion, so we will create a `Partwise.Measure`. Otherwise, we would create a `Timewise.Measure`.

```Swift
let measure = Partwise.Measure(
number: "1",
musicData: [
.attributes(attributes),
.note(note)
]
)
```

We have all of the musical information under control, so let's do some administrative work to declare who is playing this music. We will start by creating a single part. Again, as we are traversing the score in a partwise fashion, we will create a `Partwise.Part`, rather than a `Timewise.Part`. We can keep track of this part by its identifier, `"P1"`.

```Swift
let part = Partwise.Part(id: "P1", measures: [measure])
```

Almost there. We just need to declare all of the parts in our composition so that we can keep track of things at a high level.

```Swift
let header = Header(
partList: [
.part(ScorePart(id: "P1", name: "Music"))
]
)
```

We can package up our `header` and our solo `part` into a `Partwise` traversal of the work.

```Swift
let traversal = Partwise(header: header, parts: [part])
let score = Score.partwise(traversal)
```

And then place our traversal inside a `Score`.
### 🧬 Decoding musicXML into a `Score`

```Swift
let score = Score(traversal: .partwise(traversal))

```

Et voilà, we have willed into being a `MusicXML` value which represents our rigorous masterpiece.

```Swift
let musicXML = MusicXML(score)
```

### 🧬 Decoding musicXML into a `MusicXML.Score`

You can decode a `MusicXML` structure in a variety of ways:
You can decode a `Score` in a variety of ways:

```Swift
let fromData: MusicXML = try MusicXML(data: data)
let fromString: MusicXML = try MusicXML(string: string)
let fromURL: MusicXML = try MusicXML(url: url)
let fromData = try Score(data: data)
let fromString = try Score(string: string)
let fromURL = try Score(url: url)
```

If you decode the musicXML representation of our "Hello, world!" composition, you will get a value equivalent to the one you have built by hand above.

### 🚧 Work-in-progress: Encoding a `MusicXML.Score` into musicXML
### 🚧 Work-in-progress: Encoding a `Score` into musicXML

[Pre-release version 0.3.0](https://github.com/dn-m/MusicXML/milestone/1) will see the completion of the encoding from a `MusicXML.Score` into the musicXML format.
[Pre-release version 0.3.0](https://github.com/dn-m/MusicXML/milestone/1) will see the completion of the encoding from a `Score` into the musicXML format.


## Getting Started
Expand Down
2 changes: 1 addition & 1 deletion Sources/MusicXML/Complex Types/Partwise/Partwise.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ extension Partwise: Codable {
self.header = try Header(from: decoder)
self.parts = try container.decode([Part].self, forKey: .parts)
// There is not currently a way for the `XMLDecoder` to check against the case of the
// `Traversal` type at the top-level. A `Partwise` traversal must have at least one part.
// `Score` type at the top-level. A `Partwise` traversal must have at least one part.
guard !self.parts.isEmpty else {
throw DecodingError.typeMismatch(
Partwise.self,
Expand Down
94 changes: 49 additions & 45 deletions Sources/MusicXML/Complex Types/Score.swift
Original file line number Diff line number Diff line change
@@ -1,55 +1,59 @@
//
// Score.swift
// MusicXML
//
// Created by James Bean on 12/3/18.
//
// MusicXML score.mod module
// Version 3.1
//
// Copyright © 2004-2017 the Contributors to the MusicXML
// Specification, published by the W3C Music Notation Community
// Group under the W3C Community Final Specification Agreement
// (FSA):
//
// https://www.w3.org/community/about/agreements/final/
//
// A human-readable summary is available:
//
// https://www.w3.org/community/about/agreements/fsa-deed/
//
// > The score is the root element for the DTD. It includes
// > the score-header entity, followed either by a series of
// > parts with measures inside (score-partwise) or a series
// > of measures with parts inside (score-timewise). Having
// > distinct top-level elements for partwise and timewise
// > scores makes it easy to ensure that an XSLT stylesheet
// > does not try to transform a document already in the
// > desired format. The document-attributes entity includes the
// > version attribute and is defined in the common.mod file.
//
// <![ %partwise; [
// <!ELEMENT score-partwise (%score-header;, part+)>
// <!ATTLIST score-partwise
// %document-attributes;
// >
// TODO: Support Score document-attributes
public struct Score {
public let traversal: Traversal

public init(traversal: Traversal) {
self.traversal = traversal
}
}
///
/// Score.swift
/// MusicXML
///
/// Created by James Bean on 12/3/18.
///
/// MusicXML score.mod module
/// Version 3.1
///
/// Copyright © 2004-2017 the Contributors to the MusicXML
/// Specification, published by the W3C Music Notation Community
/// Group under the W3C Community Final Specification Agreement
/// (FSA):
///
/// https://www.w3.org/community/about/agreements/final/
///
/// A human-readable summary is available:
///
/// https://www.w3.org/community/about/agreements/fsa-deed/
///

/// Either a `partwise` or `timewise` traversal of a MusicXML score.
public enum Score: Equatable {

extension Score: Equatable { }
/// The `partwise` traversal of a MusicXML score.
case partwise(Partwise)

/// The `timewise` traversal of a MusicXML score.
case timewise(Timewise)
}

extension Score: Codable {

// MARK: - Codable

enum CodingKeys: String, CodingKey {
case partwise = "score-partwise"
case timewise = "score-timewise"
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .partwise(value):
try container.encode(value, forKey: .partwise)
case let .timewise(value):
try container.encode(value, forKey: .timewise)
}
}

public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self.traversal = try container.decode(Traversal.self)
do {
self = .partwise(try container.decode(Partwise.self))
} catch {
self = .timewise(try container.decode(Timewise.self))
}
}
}
2 changes: 1 addition & 1 deletion Sources/MusicXML/Complex Types/Technique.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public enum Technique {
case pullOff(HammerOnPullOff)
case snapPizzicato(EmptyPlacement = EmptyPlacement())
case stopped(EmptyPlacement = EmptyPlacement())
case string(MusicXML.String = MusicXML.String()) // FIXME: Swift.String vs. MusicXML.String
case string(MusicXML.String = MusicXML.String())
case tap(PlacementText)
case thumbPosition(EmptyPlacement = EmptyPlacement())
case toe(HeelToe)
Expand Down
2 changes: 1 addition & 1 deletion Sources/MusicXML/Complex Types/Timewise/Timewise.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ extension Timewise: Codable {
self.header = try Header(from: decoder)
self.measures = try container.decode([Measure].self, forKey: .measures)
// There is not currently a way for the `XMLDecoder` to check against the case of the
// `Traversal` type at the top-level. A `Timewise` traversal must have at least one measure.
// `Score` type at the top-level. A `Timewise` traversal must have at least one measure.
guard !self.measures.isEmpty else {
throw DecodingError.typeMismatch(
Partwise.self,
Expand Down
47 changes: 0 additions & 47 deletions Sources/MusicXML/Complex Types/Traversal.swift

This file was deleted.

4 changes: 2 additions & 2 deletions Sources/MusicXML/Decoding/Decoding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation
import XMLCoder

extension MusicXML {
extension Score {

public enum Error: Swift.Error {
case invalidMusicXMLString(Swift.String)
Expand All @@ -29,6 +29,6 @@ extension MusicXML {

/// Creates a `MusicXML` model from the given MusicXML-formatted `data`.
public init(data: Data) throws {
self.score = try XMLDecoder(trimValueWhitespaces: false).decode(Score.self, from: data)
self = try XMLDecoder(trimValueWhitespaces: false).decode(Score.self, from: data)
}
}
18 changes: 2 additions & 16 deletions Sources/MusicXML/Simple Types/MusicXML.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,5 @@
// Created by James Bean on 12/3/18.
//

/// A MusicXML document.
public struct MusicXML {

// MARK: - Instance Properties

/// The MusicXML.Score which is contained within a MusicXML document.
public let score: Score

// MARK: - Initializers

public init(_ score: Score) {
self.score = score
}
}

extension MusicXML: Equatable { }
/// The `MusicXML` "namespace".
public enum MusicXML { }
8 changes: 3 additions & 5 deletions Tests/MusicXMLTests/HelloWorld.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class HelloWorld: XCTestCase {
</part>
</score-partwise>
"""
let decoded = try MusicXML(string: xml)
let decoded = try Score(string: xml)

// Create the note
let note = Note(pitch: Pitch(step: .c, octave: 4), duration: 4, type: .whole)
Expand Down Expand Up @@ -80,10 +80,8 @@ class HelloWorld: XCTestCase {
// Create the traversal
let traversal = Partwise(header: header, parts: [part])
// Create the score
let score = Score(traversal: .partwise(traversal))
// Create the MusicXML
let musicXML = MusicXML(score)
let score: Score = .partwise(traversal)

XCTAssertEqual(decoded, musicXML)
XCTAssertEqual(decoded, score)
}
}
2 changes: 1 addition & 1 deletion Tests/MusicXMLTests/LilyPondTests/LilyPondTestSuite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class LilyPondTests: XCTestCase {
let sourceURL = sourceDir.appendingPathComponent(source)
do {
let start = now()
let parsed = try MusicXML(url: sourceURL)
let parsed = try Score(url: sourceURL)
let duration = now() - start

publishSuccessfulParsing(for: "\(traversal)/\(source)", in: duration)
Expand Down
2 changes: 1 addition & 1 deletion Tests/MusicXMLTests/LilyPondTests/TupletsTremoloTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ class TupletsTremoloTest: XCTestCase {
<!--=========================================================-->
</score-partwise>
"""
let _ = try MusicXML(string: xml)
let _ = try Score(string: xml)
}

func testTremolo() throws {
Expand Down
2 changes: 1 addition & 1 deletion Tests/MusicXMLTests/ScoreTests/ScoreTestExpectation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ import XCTest
import MusicXML

protocol ScoreTestExpectation: AnyObject {
static var expected: MusicXML { get }
static var expected: Score { get }
}
Loading

0 comments on commit 67feb23

Please sign in to comment.