dn-m / MusicXML

Implementation of the musicXML specification in Swift
MIT License
74 stars 20 forks source link

Support Partwise <-> Timewise conversion #164

Closed jsbean closed 5 years ago

jsbean commented 5 years ago

Traversing a MusicXML score either in a partwise or timewise fashion has its benefits.

Let's enable conversion between Partwise and Timewise elements.

jsbean commented 5 years ago

This could look something like:

extension Partwise {
    func toTimewise() -> Timewise { ... }
}

extension Timewise {
    func toPartwise() -> Partwise { ... }
}

extension Traversal {
    func asPartwise() -> Partwise { // only do the conversion if necessary }
    func asTimewise() -> Timewise { // only do the conversion if necessary }
}
jsbean commented 5 years ago

@DJBen, @bwetherfield, any gripes with an API like that?

DJBen commented 5 years ago

IMO these two APIs are sufficient.

extension Partwise {
    func toTimewise() -> Timewise { ... }
}

extension Timewise {
    func toPartwise() -> Partwise { ... }
}

We can create a new Traversal trivially by .timewise(partwise.toTimewise()) and vice versa. What do you think

jsbean commented 5 years ago

The use case I was thinking of would be something like this:

// Don't know what that traversal is
let musicXML = MusicXML(url: URL(string: "https://whatever.info")!)
// I want it timewise
for measure in musicXML.score.traversal.asTimewise().measures {
    // render measure
}

Of course you could do it like this:

let traversal = musicXML.score.traversal
let timewise: Timewise
switch traversal {
case .timewise(value):
    timewise = value
case .partwise(value):
    timewise = value.toTimewise()
}
for measure in timewise.measures {
    // render measure
}

But the one-liner is nice.

jsbean commented 5 years ago

(Potentially off-topic: I think we are currently at least one level too-nested with the structure as it is. I think we should get rid of either MusicXML or Score or Traversal and squash things down slightly.) See #166.

jsbean commented 5 years ago

As far as the API, @DJBen, do you prefer:

extension Timewise {
    func toPartwise() -> Partwise { ... }
}

Or something like this:

extension Partwise {
    init(_ timewise: Timewise) { ... }
}