cuthbertLab / music21

music21 is a Toolkit for Computational Musicology
https://www.music21.org/
Other
2.14k stars 402 forks source link

Support Staff-Tuning tags in musicxml with sensible default #778

Open louisbigo opened 3 years ago

louisbigo commented 3 years ago

music21 version

6.5.0

Problem summary Hi,

It seems that the musicXML writer can produce mistakes when the m21 stream corresponds to a tablature.

The parsing seems ok (string/fret informations seem correctly stored in the m21 stream in articulations.FretIndication / articulations.StringIndication). However, the written musicXML file is invalid (as indicated when opening with MuseScore for example).

When looking into it, it seems that unexpected fret tags (more precisely <fret>0</fret>) have been added in place of technical indications (like hammer-on, pull-off) - see example below. I would not be suprised that multiple (and distinct) fret indications for one single note induces this invalidity.

I guess that guitar specific technics such as hammer-on/pull-off are not yet implemented in m21. I think that simply omitting them (instead of replacing them by non-sense <fret>0</fret>) would produce a valid musicXML and might be a good option until that time.

Thanks again for this fantastic tool !

Steps to reproduce

The musicXML page dedicated to tab (https://www.musicxml.com/tutorial/tablature/fret-and-string/) provides a simple test file (tab1.musicxml).

from music21 import *
tabmxml = converter.parse('./tab1.musicxml')
tabmxml.write('musicxml','./tab1-m21.musicxml')

In tab1.musicxml we have (l.321) :

<technical>
  <pull-off number="1" type="stop"/>
  <pull-off number="1" type="start">P</pull-off>
  <string>3</string>
  <fret>7</fret>
</technical>

and in the analogue place of the file written by m21 (tab1-m21.musicxml) we have (l.288) :

<technical>
  <fret>0</fret>
  <fret>0</fret>
  <string>3</string>
  <fret>7</fret>
</technical>
louisbigo commented 3 years ago

Thank you for this modification. Unfortunately it did not solve the problem, but by doing some manual tests, I realized that the problem was not caused by the unexpected <fret>0</fret> tags as I initially presumed but by the lack of information regarding the tuning of the guitar. The MusicXML written by m21 is displayed correctly in MuseScore if the follwing lines are added within the <staff-details> element of the guitar part :

        <staff-details print-object="yes">
          <staff-lines>6</staff-lines>
          <staff-tuning line="1">
            <tuning-step>E</tuning-step>
            <tuning-octave>2</tuning-octave>
          </staff-tuning>
          <staff-tuning line="2">
            <tuning-step>A</tuning-step>
            <tuning-octave>2</tuning-octave>
          </staff-tuning>
          <staff-tuning line="3">
            <tuning-step>D</tuning-step>
            <tuning-octave>3</tuning-octave>
          </staff-tuning>
          <staff-tuning line="4">
            <tuning-step>G</tuning-step>
            <tuning-octave>3</tuning-octave>
          </staff-tuning>
          <staff-tuning line="5">
            <tuning-step>B</tuning-step>
            <tuning-octave>3</tuning-octave>
          </staff-tuning>
          <staff-tuning line="6">
            <tuning-step>E</tuning-step>
            <tuning-octave>4</tuning-octave>
          </staff-tuning>
        </staff-details>

Although this correspond to standard guitar tuning, I don't know what would be the proper way to extract these informations from the m21 stream in case of non-standard tunings (like open D or others). Could we consider in a first step to add by default this standard tuning block as it will certainly cover a large majority of guitar tabs ?

mscuthbert commented 3 years ago

Reopening because of the problem noted above. Renaming issue. Might be a little while, since we're not fully supporting fretboards yet, though Luke Poeppel did some great work on this getting us ready to do so later.

jacobtylerwalls commented 3 years ago

The question is whether we need a new StaffTuning object, or whether we can just translate to/from fretboard tuning arrays:

>>> fb = tablature.GuitarFretBoard()
>>> fb.tuning
[<music21.pitch.Pitch E2>, <music21.pitch.Pitch A2>, <music21.pitch.Pitch D3>, <music21.pitch.Pitch G3>, <music21.pitch.Pitch B3>, <music21.pitch.Pitch E4>]

Seems easier to me to build on what we have -- read the musicxml file for a tuning, make a pitch array from the entries, if it matches one of the known fretboards, construct it, and drop it in the beginning of the part. Maybe a method in the tablature module fretBoardFromTuning(pitches) that returns a standard or custom fretboard from some pitch array.

Then in the export module, look for a FretBoard object at 0.0 (the first one), if so, translate the .pitches array to these tags. (Certainly we can handle the default GuitarFretBoard first and leave TODOs for exporting the custom ones.)

@louisbigo any thoughts on directions here, or interest in making a more detailed proposal?