Code Monkey home page Code Monkey logo

musicxml's Introduction

dn-m

Application targets for the dn-m project.

musicxml's People

Contributors

bwetherfield avatar djben avatar jsbean avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

musicxml's Issues

How to play music from MusicXML

Can you help me please. I write a music editor. Please tell me whether it is realistic to play music from this format in Audio Toolbox iOS? And how can I do this? Thank you.

[Discussion] Use Codable API?

As of 0.1.0, SWXMLHash is used to deserialize musicXML strings into a traversable data structure. This package works for deserialization, though provides no API for serializing custom data structures into the XML format.

A goal of this project is to provide APIs both for reading, as well as writing, musicXML files.

It might make sense to conform the model types to Codable, for decoding as well as encoding. This will require building a custom XML Decoder, which would perhaps be the Swiftiest way to achieve this.

Implement encoding for all types

In order for #154, and 0.3.0 to happen, we need to implement encoding for all types.

Knowing what we know now (which is considerably more than when we started with the decoding process), @bwetherfield, is there anything we can do with Sourcery to jump start this process (I know you've already got a start on it)?

[Model] Manage Attributes element and offsets

The Attributes element can occur anywhere (downbeat of a measure, mid-measure). As such, we need to store these attributes by offset and Part.

struct Score {
    typealias Offset = // ?
    let attributes: [Pair<ScorePart,Offset>: Set<Attribute>]
}

[Model] Implement part-wise and time-wise variants of Score Elements

As per the musicXML spec, a score can be traversed in two ways:

  • "time-wise" (through measures which contain parts)
  • "part-wise" (through parts which contain measures)

As of 0.1.0, only a primitive "part-wise" implementation is made, as this is the mode of traversal for the "Hello World" example.

The affected types are Measure and Part, which both must have part-wise and time-wise variants implemented.

Underlying each type could be a Traversal enum, in the same way that MusicXML.Score is currently implemented.

Handle Attribute Groups better

Currently, attribute groups (e.g., print-style-align) are implemented as structs (e.g., PrintStyleAlign).

As such, they are nested into their parent type. However, the properties of attribute groups exist on the top-level of the parent type. This adds considerable complexity to the encoding/decoding of types.

Instead of nesting attribute groups as properties in complex types, we should declare attribute groups as protocols, and manually implement the properties in each type.

In other words, instead of:

struct SomeType {
    let printStyleAlign: PrintStyleAlign
}

Try this:

protocol PrintStyleAlign {
    var defaultX: Double? { get }
    var defaultY: Double? { get }
    ...
}
struct SomeType: PrintStyleAlign {
    var defaultX: Double?
    var defaultY: Double?
    ...
    var somethingElse: String?
}

This will require some additional keystrokes, but will make the encode/decode process much more straightforward. We can probably find/replace the current let printStyleAlign: PrintStyleAlign with the list of required properties.

Alternatives: Use a class-based inheritance model. I would rather not dip into reference semantics just for convenience.

[Decoding] Accidental not decoding properly

The Accidental structure is defined in MusicXML as:

<!ELEMENT accidental (#PCDATA)>
<!ATTLIST accidental
    cautionary %yes-no; #IMPLIED
    editorial %yes-no; #IMPLIED
    %level-display;
    %print-style;
    %smufl;
>

An example of this in the wild is:

<accidental editorial="yes" cautionary="yes">flat</accidental>

Due to CoreOffice/XMLCoder#12, elements which have both values (e.g., the #PCDATA which is "sharp", "flat", etc.) and attributes (e.g., cautionary, editorial) are not yet decodable.

Allow multiple Dynamics values per DirectionType

The DirectionType.dynamics case currently holds onto a single Dynamics as its associated value.

In the MusicXML spec, however, there should be able to be a sequence of Dynamics values. In #126, we were able to decode a single Dynamics value, which seems to be the vastly more common case.

@DJBen, @bwetherfield, let me know if you've got thoughts on how to deal with this.

[LilyPond Test Suite] Introduce Rhythm Tests

Copy the Partwise and Timewise .xml rhythm-centric test files into the test suite as String values.

  • Start by creating a new directory called 03_Rhythm in each of the Tests/Partwise and Tests/Timewise directories.

  • Add new XCTestCase subclasses called Partwise_03_Rhythm/Timewise_03_Rhythm located in their respective parent directories.

  • For each test, add a new file to the Tests directory, with the naming like Partwise_03_Rhythm_A_Durations.swift.

  • In that file, extend the Partwise_03_Rhythm XCTestCase subclass with a computed String property with the name A_Durations:

extension Partwise_03_Rhythm {
    var A_Durations: String {
        """
        <xml/><goes/><here/>
        """
    }
}
  • Within the Partwise_03_Rhythm XCTestCase subclass, add a test:
class Partwise_03_Rhythm: XCTestCase {
    func test_03_Rhythm() throws {
        let _ = try MusicXML(string: A_Durations)
        // repeat for each tests
    }
}

These not-so-rigorous tests will expose glaring decoding issues. We can move onto details incrementally.

Improve documentation

Add some examples of parsing, manipulating, and exporting very basic MusicXML samples to the README.

Scrape MusicXML docs for code coverage

Since every page of the musicXML docs contains an example, would it be possible to scrape this, for total code coverage, similar to what we have done with the LilyPond tests. I am not sure if there is a public repo for the docs - perhaps that would be an easier place to start!

[Tests] Add tests for LilypondTestSuite

Many sample MusicXML files from the Lilypond project have been added in Tests/LilypondTestSuite.

Because Swift Package Manager does not yet support bundled resources, convert each of these to an equivalently-named .swift file, which contains a String version of the XML. This seems like good practice in any case, as it also removes one layer of testing (that the extraction of resources from a bundle succeeds).

  • Pitches (#12)
  • Rests (#39)
  • Rhythm (#43)
  • Time Signatures (#44)
  • Clefs (#48)
  • Key Signatures (#46)
  • Staff Details (#49)
  • Chords
  • Noteheads
  • Tuplets (#57)
  • Grace Notes
  • Directions
  • Notations (#59)
  • Arpeggio
  • Spanners
  • Parts
  • Repeats
  • Measures
  • Header
  • Layout
  • Lyrics
  • TAB
  • Transposing
  • Percussion
  • Figured Bass
  • Accordion
  • Compressed
  • Ignore Beaming

Audit MusicXML -> Score -> Traversal structure

I propose that we are in-too-deep with regards to the structure of our MusicXML.

The current state-of-the-art is like this:

let musicXML = MusicXML(Score(traversal: .partwise(partwise)))

I don't think MusicXML is giving us anything at the moment.

Instead, the top level could be Score, and the decoding / encoding should happen in relation to that.

let musicXML = Score(.partwise(partwise), documentAttributes: ...)
let score = try Score(string: "...")
try score.write(to: url)

These initializers aren't valid yet, but possibly should be.

[Decoding] MusicData not decoding properly

The music-data element in MusicXML is a collection of any number of values within a set of possible types

<!ENTITY % music-data
	"(note | backup | forward | direction | attributes |
	  harmony | figured-bass | print | sound | barline | 
	  grouping | link | bookmark)*">

This is representable as an enum with associated values in Swift (really, it's a union type):

enum MusicDatum {
    case note(Note)
    case attributes(Attributes)
    ...
}

Due to CoreOffice/XMLCoder#25, arrays of enums with associated values are not yet decodable.

Audit placement of Header

Currently both Timewise and Partwise have a property header: Header. Isn't this better suited at the Score level?

Fix Key.NonTraditional decoding re: optional key-accidental values

This fix presented #120 does not address cases like the one below. As per the spec, it would be possible to see something like this:

<key>
    <key-step>B</key-step>
    <key-alter>-1</key-alter>

    <key-step>E</key-step>
    <key-alter>-2</key-alter>
    <key-accidental>slash-flat</key-accidental>

    <key-step>A</key-step>
    <key-alter>-2</key-alter>
    <key-accidental>slash-flat</key-accidental>

    <key-step>F</key-step>
    <key-alter>2</key-alter>
</key>

With this fix, this example would not parse correctly.

@DJBen presented:

See my latest PR #124, I encountered the same issue with harmony having imploded group that is a list: harmony-chord.

I've tried many decoder combinations but none avail. My final solution is to parse them into a list of enums like below

enum KeyComponent {
 case keyStep(KeyStep)
 case keyAlter(KeyAlter)
 case keyAccidental(KeyAccidental)
}

and reassemble it into multiple Keys (see #124).

@bwetherfield adds:

That looks good! Would something like this be possible to enforce order?

while !valuesContainer.isAtEnd {
    do {
       keyComponents.append(.keyStep(try valuesContainer.decode(KeyStep.self)))
       keyComponents.append(.keyAlter(try valuesContainer.decode(KeyAlter.self)))
        keyComponents.append(.keyAccidental(try valuesContainer.decodeIfPresent(KeyAccidental.self)))
   } catch {
       break
   }
}

This would work with:

enum KeyComponent {
 case keyStep(KeyStep)
 case keyAlter(KeyAlter)
 case keyAccidental(KeyAccidental?)
}

[API] Initializers for `value`-holding types

There are many types which have properties named value, which are generally "Simple Types", and are what XMLCoder calls "Coding Key Value Intrinsic". In XML parlance, complex types "extend" "Simple Types" with attributes.

For these types, we can remove the attribute label in initializers, as these types are required and usually self-explanatory.

For example, the complex type stem "extends" the stem-value type.

I suggest we implement initializers for types like this:

extension Stem {
    init(_ value: StemValue, ...) {
        self.value = value
        ...
    }
}

Which make the call site look something like this:

let stem = Stem(.up)

@DJBen, does this work for or against any of your thoughts on the matter?

Chord not decoding properly

This may be related to #41, wherein empty elements don't currently play nice with XMLCoder.

In this case the <chord/> element does not even make it to the XMLKeyedDecodingContainer when it is the first element. However, if it is moved below <pitch>, it works.

Fix Key.NonTraditional model

Currently, Key.NonTraditional is defined as such:

struct NonTraditional {
    public var step: Step
    public var alter: Double
    public var accidental: AccidentalValue
}

However, NonTraditional should hold an array of these values (step, alter, and accidental), instead of a single instance. Like so:

struct NonTraditional {
    struct AlteredTone {
        let step: Step
        let alter: Double
        let accidental: AccidentalValue
    }
    let alteredTones: [AlteredTone]
}

This should resolve the currently non-parsing 13c-KeySignatures-NonTraditional.xml test.

Decide top level element using regex match against <!DOCTYPE (score-partwise|score-timewise)

Currently we have no way to know the top element to be whether score-partwise or score-timewise. We work around by attempting score-partwise decoder first and upon catching any error, falling back to score-timewise.

This sweeps potential decoding issues under the rug. For example, if our decoder has a bug when decoding partwise score, it will be caught by this logic and attempting to use score-timewise to decode it and fails unsurprisingly, completely swallowing any errors and making our debugging process difficult.

I inquired in XMLCoder for ways to decode <!DOCTYPE>, and get negative answers. I am thinking to use a simple regex matcher to extract top-level tag name. It should be robust enough given all music xml DTD declarations are fairly consistent.

What do you think? Looking forward to your opinions.

[Model] Refactor Attributes out of Measure

Currently, Measure contains an array of Attribute values. The Attribute values are not actually applicable to a Measure specifically, but can be defined at any point (incl. mid-measure).

Instead, model Measure around these parameters:

<!ATTLIST measure
    number CDATA #REQUIRED
    text CDATA #IMPLIED
    implicit %yes-no; #IMPLIED
    non-controlling %yes-no; #IMPLIED
    width %tenths; #IMPLIED
    %optional-unique-id;
>

Improve Note initializers

Note has a complex structure. In order to create one, you need to dive down a couple enums deep.

Let's provide convenience initializers for each of these different Kinds. Though it expands combinatorially, it may be nice to provide a convenience initializer for each PitchUnpitchedOrRest variant of each Kind.

Create a MusicXMLDecoder: XMLDecoder subclass

Now that we are disabling .trimValueWhitespaces from our decoder, it may make sense to create an XMLDecoder subclass MusicXMLDecoder.

This way we can use MusicXMLDecoder when testing atomic units, so we know we are working with the same thing when decoding a whole MusicXML object.

This will also allow us to wipe XMLCoder from testing, which has seemed slightly out of place.

"Je fais un rêve"

The Après un rêve test sample is not decoding properly. Let's smoke it out.

Use XML(En|De)Coder convert(To|From)KebabCase

The XML(En|De)Coder is able to convert(To|From)KebabCase automatically.

We are currently doing this manually within the CodingKeys enum of each type (as necessary). We could standardize this by configuring XML(En|De)Coder to do it for us.

This seems like it lives at the border of YAGNI, but @bwetherfield has found in his auto-choice-coding-key Sourcery expeditions that human error can be corrected by large-scale automations.

@bwetherfield, @DJBen, whaddya think?

Implement Ties struct

A Normal note may have 0, 1, or 2 <tie> elements.

We should represent this as a struct with two optional fields:

struct Ties {
    let start: Tie?
    let stop: Tie?
}

and add a ties property to Note.Normal.

Handle Empty* types better

There are a few structures which are just bags of attribute groups (e.g., EmptyPrintStyleAlign, EmptyFont, etc.)

These things seem like implementation details that should only be knowable within the encoding / decoding processes, and not really appropriate to be part of the API.

We could either replace these Empty* types with the attribute groups themselves (e.g., PrintStyleAlign), or perhaps create custom types?

Assert in LilyPondTests.testAll()

Now that all of the LilyPond tests parse without throwing up, we should assert that all of the tests continue to parse without throwing up.

Make LilyPond tests round-trip to test encoding

We currently only test that we can decode the LilyPond test suite without throwing errors. Now, let's up the ante by making the LilyPond tests perform the following maneuver:

  • Decode MusicXML files from LilyPond test suite
  • Encode the decoded MusicXML model
  • Re-decode the encoded MusicXML data
  • Compare the originally decoded and re-decoded MusicXML model

An initiative on standardizing struct initializers

As @jsbean commented on issue #98 and in README, to make this project truly usable by other third party projects, we want to expose publicly available initializers, otherwise the structs are not initializable.

Currently most of the structs are public, but lack initializers. Swift auto-synthesizes internal default initializer for us.

We never run into initialization issues that often because our tests uses @testable import, which grant internal level access of the structs. However when I try to use MusicXML as a dependency from a third party project, I get all kinds of

ClassName is inaccessible due to 'internal' protection level

Thus I want to envision a standard of initializers that we all follow, which is of course subject to debate.


  1. Each struct should provide a default public initializer that covers all the parameters in each struct.
    1.1. For optional fields, the public initializer will provide a default value of nil.
    1.2. Alternative initializers can be kept internal for now.

  2. Declare all properties const (by using let instead of var). Unless there are necessary places that requires var (which we should discuss case by case), there are no need for mutability in most of the cases. We currently use var before because we don't want to provide nil to the auto-synthesized initializer. But now we define our default initializer and default optional fields to nil, there are no reason to use var anymore.

Here's an example that sums up the standard in 1 and 2.

public struct Example {
     public let requiredField: String
     public let optionalField: Int?

    public init(requiredField: String, optionalField: Int? = nil) { 
        self.requiredField = requiredField
        self.optionalField = optionalField
    }
}

// Caller
Example(requiredField: "aaa")
Example(requiredField: "aaa", optionalField: 2)
  1. Create initializer tests.
    These tests should use regular import, not @testable import, aiming to verify that each public struct is initializable publicly.

Here's an example.

import XCTest
// Do not use @testable
import MusicXML

class NoteInitializerTests: XCTestCase {
    func testPitch() {
        _ = Pitch(step: .d, octave: 3)
        _ = Pitch(step: .d, alter: 1, octave: 4)
    }
}

If we all agree, we can make improvement toward this direction and enforce how future struct are initialized. I welcome your input and comments.

Notations not decoding properly

Due to XMLCoder #122, choice element cases like Notations.Notation.tuplet(Tuplet), where the associated type (Tuple) has attributes, are not decoded properly.

Essentially all of the Notation cases are currently unsupported, because they use the same construct.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.