Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalize clef handling #15

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 48 additions & 34 deletions mei2ly.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,27 @@
<xsl:key name="isBeamEnd" match="mei:beam" use=".//*[self::mei:note or self::mei:rest or self::mei:chord or self::mei:space][last()]/generate-id()"/>
<xsl:key name="isBeamEnd" match="@beam[contains(., 't')]" use="generate-id(..)"/>
<xsl:key name="isBeamEnd" match="mei:beamSpan[not(@beam.with)]" use="key('idref', @endid)/generate-id()"/>
<xsl:variable name="durationalTags" select="('bTrem', 'chord', 'fTrem', 'halfmRpt', 'mRest', 'mSpace', 'note', 'rest', 'space', 'beam', 'beatRpt', 'mRpt', 'mRpt2', 'multiRest', 'multiRpt', 'tuplet')"/>
<xsl:key name="staffDefByFirstAffectedElement" match="mei:staffDef">
<xsl:variable name="hasPrecedingLayerContent" as="xs:boolean"
select="ancestor::mei:layer and preceding-sibling::mei:*[local-name()=$durationalTags]"/>
<xsl:variable name="firstAffectedLayerContentElement"
select=".[$hasPrecedingLayerContent]/following-sibling::mei:*[name()=$durationalTags][1]"/>
<xsl:choose>
<xsl:when test="$firstAffectedLayerContentElement">
<!-- This staffDef takes effect in the middle of a layer -->
<xsl:value-of select="$firstAffectedLayerContentElement/generate-id()"/>
</xsl:when>
<xsl:when test="ancestor::mei:staff and not($hasPrecedingLayerContent)">
<!-- This <staffDef> affects the <staff> element it lives in. -->
<xsl:value-of select="ancestor::mei:staff[1]/generate-id()"/>
</xsl:when>
<xsl:otherwise>
<!-- This <staffDef> takes effect in the next <staff> -->
<xsl:value-of select="following::mei:staff[@n=current()/@n][1]/generate-id()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:key>
<xsl:template match="/">
<xsl:text>\version "2.18.2"&#10;</xsl:text>
<xsl:text>#(ly:set-option 'point-and-click #f)&#10;</xsl:text>
Expand Down Expand Up @@ -169,16 +190,7 @@
<xsl:call-template name="setBarNumber" />
</xsl:if>
<!-- add clef change -->
<xsl:if test="generate-id(preceding::mei:staffDef[@n = $staffNumber][@clef.shape][1]/following::mei:measure[1]) = $currentMeasure">
<xsl:call-template name="setClef">
<xsl:with-param name="clefColor" select="preceding::mei:staffDef[@n = $staffNumber][@clef.shape][1]/@clef.color" />
<xsl:with-param name="clefDis" select="preceding::mei:staffDef[@n = $staffNumber][@clef.shape][1]/@clef.dis" />
<xsl:with-param name="clefDisPlace" select="preceding::mei:staffDef[@n = $staffNumber][@clef.shape][1]/@clef.dis.place" />
<xsl:with-param name="clefLine" select="preceding::mei:staffDef[@n = $staffNumber][@clef.shape][1]/@clef.line" />
<xsl:with-param name="clefShape" select="preceding::mei:staffDef[@n = $staffNumber][@clef.shape][1]/@clef.shape" />
</xsl:call-template>
<xsl:text>&#10;&#32;&#32;</xsl:text>
</xsl:if>
<xsl:apply-templates select="(key('staffDefByFirstAffectedElement', generate-id())/(@clef.shape, mei:clef))[last()]"/>
<!-- add key signature change -->
<xsl:if test="generate-id(ancestor::mei:measure/preceding-sibling::*[contains(local-name(),'Def')][@*[starts-with(name(),'key')]][1]/following-sibling::mei:measure[1]) = $currentMeasure">
<xsl:call-template name="setKey">
Expand Down Expand Up @@ -280,10 +292,10 @@
</xsl:template>
<!-- MEI score element -->
<xsl:template match="mei:score">
<xsl:apply-templates select="descendant::mei:scoreDef[1]" />
<xsl:apply-templates select="descendant::mei:scoreDef[1]" mode="score-setup"/>
</xsl:template>
<!-- MEI score definition -->
<xsl:template match="mei:scoreDef">
<xsl:template match="mei:scoreDef" mode="score-setup">
<!-- lilypond score block -->
<xsl:text>\score { &lt;&lt;&#10;</xsl:text>
<xsl:if test="ancestor::mei:mdiv[1]//@source">
Expand All @@ -294,7 +306,7 @@
</xsl:for-each>
<xsl:text>)&#10;</xsl:text>
</xsl:if>
<xsl:apply-templates select="mei:staffGrp|mei:staffDef" />
<xsl:apply-templates select="mei:staffGrp|mei:staffDef" mode="score-setup"/>
<xsl:text>&gt;&gt;&#10;</xsl:text>
<!-- lilypond layout block -->
<xsl:text>\layout {&#10;</xsl:text>
Expand Down Expand Up @@ -366,7 +378,7 @@
</xsl:if>
</xsl:template>
<!-- MEI staff group -->
<xsl:template match="mei:staffGrp">
<xsl:template match="mei:staffGrp" mode="score-setup">
<xsl:text>\new StaffGroup </xsl:text>
<xsl:if test="@label or @label.abbr or child::mei:label">
<xsl:text>\with { </xsl:text>
Expand All @@ -375,11 +387,11 @@
</xsl:if>
<xsl:text>&lt;&lt;&#10;</xsl:text>
<xsl:call-template name="setStaffGrpStyle" />
<xsl:apply-templates select="mei:staffGrp|mei:staffDef" />
<xsl:apply-templates select="mei:staffGrp|mei:staffDef" mode="score-setup"/>
<xsl:text>&gt;&gt;&#10;</xsl:text>
</xsl:template>
<!-- MEI staff definitons -->
<xsl:template match="mei:staffDef">
<xsl:template match="mei:staffDef" mode="score-setup">
<xsl:variable name="mdivNumber" select="ancestor::mei:mdiv/@n" />
<xsl:variable name="staffNumber" select="@n" />
<xsl:text> \new </xsl:text>
Expand Down Expand Up @@ -457,16 +469,6 @@
</xsl:choose>
<!-- set MEILER default styles -->
<xsl:text>\set tieWaitForNote = ##t&#10; </xsl:text>
<xsl:if test="ancestor-or-self::*/@*[starts-with(name(),'clef.')]">
<xsl:call-template name="setClef">
<xsl:with-param name="clefColor" select="@clef.color" />
<xsl:with-param name="clefDis" select="@clef.dis" />
<xsl:with-param name="clefDisPlace" select="@clef.dis.place" />
<xsl:with-param name="clefLine" select="@clef.line" />
<xsl:with-param name="clefShape" select="@clef.shape" />
</xsl:call-template>
</xsl:if>
<xsl:apply-templates select="mei:clef" />
<xsl:call-template name="setKey">
<xsl:with-param name="keyTonic" select="ancestor-or-self::*/@key.pname" />
<xsl:with-param name="keyAccid" select="ancestor-or-self::*/@key.accid" />
Expand Down Expand Up @@ -559,7 +561,7 @@
<xsl:text>}&#32;</xsl:text>
</xsl:template>
<!-- MEI measure -->
<xsl:template name="measure" match="mei:measure">
<xsl:template match="mei:measure">
<xsl:value-of select="' '" />
<xsl:if test="(ancestor::mei:measure[@n and not(@metcon='false')]/@n != preceding::mei:measure[@n and not(@metcon='false')][1]/@n + 1)">
<xsl:call-template name="setBarNumber" />
Expand Down Expand Up @@ -595,13 +597,25 @@
<xml:text>\\ </xml:text>
</xsl:if>
</xsl:template>
<!-- MEI staffDef (inside musical flow) -->
<xsl:template match="mei:staffDef[ancestor::mei:layer]">
<xsl:apply-templates select="(mei:clef, @clef.shape)[1]"/>
</xsl:template>
<!-- MEI clefs -->
<xsl:template name="setClef" match="mei:clef">
<xsl:param name="clefColor" select="@color" />
<xsl:param name="clefDis" select="@dis" />
<xsl:param name="clefDisPlace" select="@dis.place" />
<xsl:param name="clefLine" select="@line" />
<xsl:param name="clefShape" select="@shape" />
<xsl:template match="mei:clef|@clef.shape">
<xsl:param name="clefColor" select="@color|../@clef.color" />
<xsl:param name="clefDis" select="@dis|../@clef.dis" />
<xsl:param name="clefDisPlace" select="@dis.place|../@clef.dis.place" />
<xsl:param name="clefLine" select="@line|../@clef.line" />
<xsl:param name="clefShape" select="@shape|../@clef.shape" />
<xsl:variable name="mei2lyClefMap">
<clef mei="G" ly="G"/>
<clef mei="F" ly="F"/>
<clef mei="C" ly="C"/>
<clef mei="perc" ly="percussion"/>
<clef mei="TAB" ly="tab"/>
<clef mei="GG" ly="GG"/><!-- Not working in v2.18 and earlier? -->
</xsl:variable>
<xsl:variable name="clefTrans">
<xsl:choose>
<xsl:when test="$clefDisPlace='above'">
Expand Down Expand Up @@ -641,7 +655,7 @@
<xsl:if test="@cautionary">
<xsl:value-of select="concat('\set Staff.forceClef = ##',substring(@cautionary,1,1),' ')"/>
</xsl:if>
<xsl:value-of select="concat('\set Staff.clefGlyph = #','&quot;clefs.',$clefShape,'&quot; ')" />
<xsl:value-of select="concat('\set Staff.clefGlyph = #','&quot;clefs.', $mei2lyClefMap/*[@mei=$clefShape]/@ly,'&quot; ')" />
<xsl:value-of select="concat('\set Staff.clefPosition = #',$clefPos,' ')" />
<xsl:value-of select="concat('\set Staff.clefTransposition = #',$clefTrans,' ')" />
<xsl:value-of select="concat('\set Staff.middleCPosition = #',$clefPos + $cOffset - $clefTrans,' ')" />
Expand Down
94 changes: 94 additions & 0 deletions tests/clefs.mei
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="http://www.music-encoding.org/schema/3.0.0/mei-all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>
<?xml-model href="http://www.music-encoding.org/schema/3.0.0/mei-all.rng" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<mei xmlns="http://www.music-encoding.org/ns/mei" meiversion="3.0.0" xmlns:meiler="NS:MEILER_TEST">
<meiHead>
<fileDesc>
<titleStmt>
<title>clef change tests</title>
</titleStmt>
<pubStmt/>
</fileDesc>
<extMeta>
<meiler:test system="bash" input="ly">
# Test strategy: We assigned ascending color values to elements in the order of appearance.
# We expect the order to be the same in the output.
testcolors=$(egrep -o 'rgb-color[^)]+' "$ly" | egrep --color=never -o '[0-9.].+')
# Clefs are flagged with a trailing 255 blue value (1 in lilypond)
cleftestcolors=$(echo "$testcolors" | grep --color=never '1$')
if echo "$cleftestcolors" | sort | uniq --count | egrep -v '^\s*1' > /dev/null
then
echo "At least one clef was output twice"
exit 1
fi
if [ $( echo "$cleftestcolors" | wc -l ) -ne 8 ]
then
echo "Number of output clefs does not match number of defined clefs"
exit 1
fi
if [ "$testcolors" != "$( echo "$testcolors" | sort )" ]
then
echo "At least one clef was output in the wrong place"
exit 1
fi
</meiler:test>
</extMeta>
</meiHead>
<music>
<body>
<mdiv>
<score>
<scoreDef meter.count="4" meter.unit="4">
<staffGrp>
<staffDef lines="5" n="1">
<clef shape="C" line="3" color="rgb(1,0,255)"/>
</staffDef>
<staffDef clef.line="4" clef.shape="F" lines="5" n="2" clef.color="rgb(100,0,255)"/>
</staffGrp>
</scoreDef>
<section>
<measure n="1">
<staff n="1">
<layer n="1">
<note dur="2" oct="5" pname="c" color="rgb(2,0,0)"/>
<clef shape="F" line="4" color="rgb(3,0,255)"/>
<rest dur="2" color="rgb(4,0,0)"/>
</layer>
</staff>
<staff n="2">
<layer n="1">
<note dur="2" oct="3" pname="f" color="rgb(101,0,0)"/>
<staffDef clef.shape="C" clef.line="3" n="2" clef.color="rgb(102,0,255)"/>
<rest dur="2" color="rgb(103,0,0)"/>
</layer>
</staff>
</measure>
<measure n="2">
<staffDef n="1" clef.shape="G" clef.line="2" clef.color="rgb(5,0,255)"/>
<staff n="1">
<layer>
<rest dur="2" color="rgb(6,0,0)"/>
<clef shape="perc" line="3" color="rgb(7,0,255)"/>
<rest dur="2" color="rgb(8,0,0)"/>
</layer>
</staff>
<staff n="2">
<staffDef n="2" clef.shape="F" clef.line="4" clef.color="rgb(104,0,255)"/>
<layer>
<beam>
<note dur="8" pname="c" oct="4" color="rgb(105,0,0)"/>
<note dur="8" pname="c" oct="4" color="rgb(106,0,0)"/>
<clef shape="G" line="2" color="rgb(107,0,255)"/>
<note dur="8" pname="c" oct="4" color="rgb(108,0,0)"/>
<note dur="8" pname="c" oct="4" color="rgb(109,0,0)"/>
</beam>
<rest dur="2" color="rgb(110,0,0)"/>
</layer>
</staff>
</measure>
</section>
</score>
</mdiv>
</body>
</music>
</mei>