ice6 / opensheetmusicdisplay

Combine `osmd_transpose` and `osmd` together to show transposing function.
https://ice6.github.io/opensheetmusicdisplay/build
BSD 3-Clause "New" or "Revised" License
0 stars 0 forks source link

DOMParser & Document & TypeScript #7

Closed ice6 closed 4 years ago

ice6 commented 4 years ago

After reviewing @leo6140 's wonderful work, I'd like to share my though.

We saw the possibility that DOMParser & Document works quite well for transposing.

We can get the transposed musixml text from the Document.

We use comparision tool like araxis merge to test the function currently, after migrating to DOMParser & Document & TypeScript, we need another way to test this routine.

We can upload some files transposed by other mature software like MuseScore or Finale, and compare with them automatically. We need a way to identify what is the equalness(function equivalence).

:)

AlbertHart commented 4 years ago

i believe that OSMD itself used a DOMParser to read the musicXML, and I am prepared to do the same. And I would like to convert our transpose_xml routine to use it if we can get it to work properly.

Can you create a small sample .htm which uses DOMParser to read a MusicXML file and the allows access to it in the Document.

And can you add the opposite function to take the parsed MusicXML file and turn it mack into a XML string which I could then save to disk, etc.

When I tried this last time - about 6 months ago, I ran into a problem. I beleive it was that items with attributes, (e.g. default-x="294.17"), like this:

<note default-x="294.17" default-y="-30.00">
        <chord/>
        <pitch>
          <step>B</step>
          <alter>-1</alter>
          <octave>2</octave>
          </pitch>
        <duration>1</duration>
        <voice>1</voice>
        <type>eighth</type>
        <stem>up</stem>
        </note>
      <note>

were either difficult to get data from, or did not convert back to an XML string properly.

So see if you can create a small .html to test this.

ice6 commented 4 years ago

OK, I will write one

ice6 commented 4 years ago

It is here, just a small demo to show the usage of DomParser's API

AlbertHart commented 4 years ago

This looks promising.

Let me see what it would take to make it work for us.

[edit] I am able to do lots of good things with this, but have added some comments here that I could really use help with,

AlbertHart commented 4 years ago

In this line:

const transposedRest = this.transposePitch(stepElem.innerHTML, 0, +octaveElem.innerHTML);

Does the unary plus operator, (+), in "+octaveElem.innerHTML" do anything.

Perhaps it forces the innerHTML to be converted to a number, the same as "Number(octaveElem.innerHTML)"?

If that is the case, I think I'll continue to use Number(octaveElem.innerHTML) because it easier to read in the code.

AlbertHart commented 4 years ago

If you get a chance, see if you can fix the logic for adding a new element so that it inserts a new line after itself (before ).

image

AlbertHart commented 4 years ago

Item FIxed

[EDIT: I am able to use 'sub_element.remove();' to remove elements I no longer want.]

alter in pitch should be added if it was not there already. I know how to do that.

But if the new alter is 0, alterElem should be removed.

const transposedNote = {octave: 3, step: 'C', alter: 1, accidental: 'flat'}
octaveElem.innerHTML = transposedNote.octave;
stepElem.innerHTML = transposedNote.step;
alterElem && (alterElem.innerHTML = transposedNote.alter);

So if alterElem was found, but the transposedNote had "alter: 0", how would I remove the old alterElem?

AlbertHart commented 4 years ago

Item FIxed

[EDIT - this thing to use here was: 'getAttribute("number")' ]

// <beam number="1">end</beam>
beam_number = beam_element.getAttribute("number");

Your example did not show how to read xml attributes such as "default-x", as in:

<note default-x="294.17" default-y="-30.00">
                            <chord/>
                            <pitch>
                            <step>B</step>
AlbertHart commented 4 years ago

Item FIxed

Nested items

Your sample, had a NOTE and processed everything in the NOTE.

But when I pass a more complete score, like the one below, it finds the in the switch statement, but does not find the items inside of it like

Can you figure out how to get the "while (target = stack.pop()) {" loop to loop through and process everything.

If the "while" loop passes everything to the switch statement, then I can grab just the things I want._

"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
<score-partwise version="3.1">
  <work>
    <work-title>2 notes</work-title>
    </work>
  <identification>
    <encoding>
      <software>MuseScore 3.4.2</software>
      <encoding-date>2020-05-25</encoding-date>
      <supports element="accidental" type="yes"/>
      <supports element="beam" type="yes"/>
      <supports element="print" attribute="new-page" type="yes" value="yes"/>
      <supports element="print" attribute="new-system" type="yes" value="yes"/>
      <supports element="stem" type="yes"/>
      </encoding>
    </identification>
  <defaults>
    <scaling>
      <millimeters>7.05556</millimeters>
      <tenths>40</tenths>
      </scaling>
    <page-layout>
      <page-height>1584</page-height>
      <page-width>1224</page-width>
      <page-margins type="even">
        <left-margin>56.6929</left-margin>
        <right-margin>56.6929</right-margin>
        <top-margin>56.6929</top-margin>
        <bottom-margin>113.386</bottom-margin>
        </page-margins>
      <page-margins type="odd">
        <left-margin>56.6929</left-margin>
        <right-margin>56.6929</right-margin>
        <top-margin>56.6929</top-margin>
        <bottom-margin>113.386</bottom-margin>
        </page-margins>
      </page-layout>
    <word-font font-family="FreeSerif" font-size="10"/>
    <lyric-font font-family="FreeSerif" font-size="11"/>
    </defaults>
  <credit page="1">
    <credit-words default-x="612" default-y="1527.31" justify="center" valign="top" font-size="24">2 notes</credit-words>
    </credit>
  <part-list>
    <score-part id="P1">
      <part-name>Piano</part-name>
      <part-abbreviation>Pno.</part-abbreviation>
      <score-instrument id="P1-I1">
        <instrument-name>Piano</instrument-name>
        </score-instrument>
      <midi-device id="P1-I1" port="1"></midi-device>
      <midi-instrument id="P1-I1">
        <midi-channel>1</midi-channel>
        <midi-program>1</midi-program>
        <volume>78.7402</volume>
        <pan>0</pan>
        </midi-instrument>
      </score-part>
    </part-list>
  <part id="P1">
    <measure number="1" width="361.76">
      <print>
        <system-layout>
          <system-margins>
            <left-margin>0.00</left-margin>
            <right-margin>748.85</right-margin>
            </system-margins>
          <top-system-distance>170.00</top-system-distance>
          </system-layout>
        </print>
      <attributes>
        <divisions>1</divisions>
        <key>
          <fifths>0</fifths>
          </key>
        <time>
          <beats>4</beats>
          <beat-type>4</beat-type>
          </time>
        <clef>
          <sign>G</sign>
          <line>2</line>
          </clef>
        </attributes>
      <note default-x="79.27" default-y="-25.00">
        <pitch>
          <step>A</step>
          <octave>4</octave>
          </pitch>
        <duration>1</duration>
        <voice>1</voice>
        <type>quarter</type>
        <stem>up</stem>
        </note>
      <note>
        <rest/>
        <duration>1</duration>
        <voice>1</voice>
        <type>quarter</type>
        </note>
      <note default-x="215.22" default-y="-15.00">
        <pitch>
          <step>C</step>
          <octave>5</octave>
          </pitch>
        <duration>1</duration>
        <voice>1</voice>
        <type>quarter</type>
        <stem>down</stem>
        </note>
      <note>
        <rest>
          <display-step>D</display-step>
          <display-octave>5</display-octave>
          </rest>
        <duration>1</duration>
        <voice>1</voice>
        <type>quarter</type>
        </note>
      <barline location="right">
        <bar-style>light-heavy</bar-style>
        </barline>
      </measure>
    </part>
  </score-partwise>
AlbertHart commented 4 years ago

Item FIxed

AHA! I found my parsing problem.

[EDIT But for things like notes in measures, I am using. element.children to get an array of child elements, and then looping through them:

                       let measure_children = measure_object.children;
                        console.log("CHILDREN: %s", measure_children.length);

                        // let's mark chords
                        // a note is in a chord if the second note element, and subsequent are marker chord
                        for (let ii = 0; ii < measure_children.length; ii++)
                        {
                            let measure_child = measure_children[ii];
                            console.log("measure CHILD %s: %s", ii, measure_child.tagName);

                            switch (measure_child.tagName) 

The "continue" at the end of the "switch 'NOTE'" means "continue and don't parse any child elements. Because they are already processed"

So for 'MEASURE', I just need to remove the continue - which will then cause the child elements, like 'NOTE' and 'ATTRIBUTES' to be parsed by the switch.

I'll try that and add a comment here it that works for me.

AlbertHart commented 4 years ago

Item FIxed - using insertAdjacentElement

Insert a new element in front of an existing element.

If the original xml looks like this:

<pitch>
  <step>C</step>
  <octave>5</octave>
 </pitch>

And I translate from C to Eb, I want the new xml to look like this. with alter in front of octave.

<pitch>
  <step>E</step>
  <alter>-1</alter>
  <octave>5</octave>
 </pitch>

You used code like this, which appends 'alter' at the end of 'pitch'. I want to insert it after step. Can you help with the insert code?

octaveElem.innerHTML = transposedNote.octave;
                        stepElem.innerHTML = transposedNote.step;
                        alterElem && (alterElem.innerHTML = transposedNote.alter);
                        if (!accidentalElem) {
                            accidentalElem = document.createElementNS('', 'accidental');
                            pitchElem.appendChild(accidentalElem);
                        } 
<pitch>
  <step>E</step>
  <alter>-1</alter>
  <octave>5</octave>
 </pitch>
AlbertHart commented 4 years ago

Item FIxed

I'll give these two functions a try...

Insert After

It looks like I can insert the new element after a specific element using "insertAdjacentElement("afterend", s)"

let h = document.getElementById("myH2");
h.insertAdjacentElement("afterend", s);

Remove Element

It looks like I can remove an element with remove()

let myobj = document.getElementById("demo");
myobj.remove();
ice6 commented 4 years ago

:+1: I am working on my company's project nowadays. It is great to see the progress you have made.

AlbertHart commented 4 years ago

The lastest version uses DomParser() to read the XML file, and does not try to read the ASCII XML file.