Skip to content

Commit

Permalink
Fix #47 Generate groove from xsl
Browse files Browse the repository at this point in the history
  • Loading branch information
infojunkie committed Aug 13, 2024
1 parent 54cead0 commit 2360f9d
Show file tree
Hide file tree
Showing 16 changed files with 463 additions and 55 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ A suite of tools to convert MusicXML scores to MIDI via [Musical MIDI Accompanim
# Converting a MusicXML score
- `npm run --silent convert:unroll song.musicxml` to unroll a MusicXML score by expanding all jumps and repeats at `stdout`
- `npm run --silent convert:mma song.musicxml` to both unroll a score and convert it to an MMA script at `stdout`
- `npm run --silent convert:groove groove-name [chords="A,B,C"] [tempo=X] [count=Y] [keysig=Z]` to generate a groove MMA script at `stdout`
- `npm run convert:midi /path/to/song.mma` to convert an MMA script to MIDI `/path/to/song.mid`
- `npm run --silent convert:timemap song.musicxml` to convert a score to a timemap JSON file at `stdout`
- `./scripts/midi-timemap.js song.mid` to convert a MIDI file to a timemap JSON file at `stdout`
Expand Down
1 change: 1 addition & 0 deletions build/groove.sef.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"N":"package","version":"30","packageVersion":"1","saxonVersion":"SaxonJS 2.6","target":"JS","targetVersion":"2","name":"TOP-LEVEL","relocatable":"true","buildDateTime":"2024-08-12T21:04:55.681-07:00","ns":"xml=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~","C":[{"N":"co","id":"0","uniform":"true","binds":"6 4 2 3 1 5","C":[{"N":"template","flags":"os","module":"groove.xsl","slots":"200","name":"Q{}groove","line":"29","expand-text":"false","sType":"* ","C":[{"N":"sequence","role":"body","sType":"* ","C":[{"N":"valueOf","sType":"1NT ","C":[{"N":"str","sType":"1AS ","val":"\nMidiText Generated by github.com/infojunkie/musicxml-midi\nKeySig "}]},{"N":"valueOf","flags":"l","sType":"1NT ","line":"32","C":[{"N":"fn","name":"string-join","role":"select","C":[{"N":"forEach","sType":"*AS ","C":[{"N":"data","sType":"*A ","C":[{"N":"mergeAdj","C":[{"N":"gVarRef","sType":"* ","name":"Q{}keysig","bSlot":"0","role":"select","line":"32"}]}]},{"N":"fn","name":"string","sType":"1AS ","C":[{"N":"dot"}]}]},{"N":"str","sType":"1AS ","val":" "}]}]},{"N":"valueOf","sType":"1NT ","C":[{"N":"str","sType":"1AS ","val":"\nTempo "}]},{"N":"valueOf","flags":"l","sType":"1NT ","line":"33","C":[{"N":"fn","name":"string-join","role":"select","C":[{"N":"forEach","sType":"*AS ","C":[{"N":"data","sType":"*A ","C":[{"N":"mergeAdj","C":[{"N":"gVarRef","sType":"* ","name":"Q{}tempo","bSlot":"1","role":"select","line":"33"}]}]},{"N":"fn","name":"string","sType":"1AS ","C":[{"N":"dot"}]}]},{"N":"str","sType":"1AS ","val":" "}]}]},{"N":"valueOf","sType":"1NT ","C":[{"N":"str","sType":"1AS ","val":"\nGroove "}]},{"N":"valueOf","flags":"l","sType":"1NT ","line":"34","C":[{"N":"fn","name":"string-join","role":"select","C":[{"N":"forEach","sType":"*AS ","C":[{"N":"data","sType":"*A ","C":[{"N":"mergeAdj","C":[{"N":"gVarRef","sType":"* ","name":"Q{}groove","bSlot":"2","role":"select","line":"34"}]}]},{"N":"fn","name":"string","sType":"1AS ","C":[{"N":"dot"}]}]},{"N":"str","sType":"1AS ","val":" "}]}]},{"N":"valueOf","sType":"1NT ","C":[{"N":"str","sType":"1AS ","val":"\nMidiMark Groove:"}]},{"N":"valueOf","flags":"l","sType":"1NT ","line":"35","C":[{"N":"fn","name":"string-join","role":"select","C":[{"N":"forEach","sType":"*AS ","C":[{"N":"data","sType":"*A ","C":[{"N":"mergeAdj","C":[{"N":"gVarRef","sType":"* ","name":"Q{}groove","bSlot":"2","role":"select","line":"35"}]}]},{"N":"fn","name":"string","sType":"1AS ","C":[{"N":"dot"}]}]},{"N":"str","sType":"1AS ","val":" "}]}]},{"N":"valueOf","sType":"1NT ","C":[{"N":"str","sType":"1AS ","val":"\nSet Duration $( round( $_Time * 60000 / $_Tempo ) )\n "}]},{"N":"let","var":"Q{}chordSeq","slot":"0","sType":"* ","line":"38","C":[{"N":"fn","name":"tokenize","sType":"*AS","ns":"= xml=~ fn=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~ ","role":"select","line":"38","C":[{"N":"treat","as":"AS","diag":"0|0||fn:tokenize","C":[{"N":"check","card":"?","diag":"0|0||fn:tokenize","C":[{"N":"cvUntyped","to":"AS","diag":"0|0||fn:tokenize","C":[{"N":"check","card":"?","diag":"0|0||fn:tokenize","C":[{"N":"data","diag":"0|0||fn:tokenize","C":[{"N":"gVarRef","name":"Q{}chords","bSlot":"3"}]}]}]}]}]},{"N":"str","val":"\\s*,\\s*"}]},{"N":"callT","bSlot":"4","sType":"* ","name":"Q{}measure","line":"39","C":[{"N":"withParam","name":"Q{}i","slot":"0","sType":"1ADI","C":[{"N":"int","val":"0","sType":"1ADI","ns":"= xml=~ fn=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~ ","role":"select","line":"40"}]},{"N":"withParam","name":"Q{}count","slot":"0","sType":"* ","C":[{"N":"gVarRef","name":"Q{}count","bSlot":"5","sType":"* ","role":"select","line":"41"}]},{"N":"withParam","name":"Q{}chordSeq","slot":"0","sType":"*","C":[{"N":"varRef","name":"Q{}chordSeq","slot":"0","sType":"*","ns":"= xml=~ fn=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~ ","role":"select","line":"42"}]}]}]}]}]}]},{"N":"co","id":"1","uniform":"true","binds":"1","C":[{"N":"template","flags":"os","module":"groove.xsl","slots":"200","name":"Q{}measure","line":"46","expand-text":"false","sType":"* ","C":[{"N":"sequence","role":"body","sType":"* ","C":[{"N":"param","name":"Q{}i","slot":"0","sType":"* ","as":"* ","flags":"","line":"47","C":[{"N":"str","sType":"1AS ","val":"","role":"select"},{"N":"supplied","role":"conversion","slot":"0","sType":"* "}]},{"N":"param","name":"Q{}count","slot":"1","sType":"* ","as":"* ","flags":"","line":"48","C":[{"N":"str","sType":"1AS ","val":"","role":"select"},{"N":"supplied","role":"conversion","slot":"1","sType":"* "}]},{"N":"param","name":"Q{}chordSeq","slot":"2","sType":"* ","as":"* ","flags":"","line":"49","C":[{"N":"str","sType":"1AS ","val":"","role":"select"},{"N":"supplied","role":"conversion","slot":"2","sType":"* "}]},{"N":"choose","sType":"* ","line":"50","C":[{"N":"gc","op":"<","comp":"GAC|http://www.w3.org/2005/xpath-functions/collation/codepoint","card":"1:1","sType":"1AB","ns":"= xml=~ fn=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~ ","line":"50","C":[{"N":"data","diag":"1|0||gc","C":[{"N":"varRef","name":"Q{}i","slot":"0"}]},{"N":"data","diag":"1|1||gc","C":[{"N":"varRef","name":"Q{}count","slot":"1"}]}]},{"N":"sequence","sType":"* ","C":[{"N":"valueOf","sType":"1NT ","C":[{"N":"str","sType":"1AS ","val":"\nMidiMark 0 Measure:"}]},{"N":"valueOf","flags":"l","sType":"1NT ","line":"52","C":[{"N":"fn","name":"string-join","role":"select","C":[{"N":"forEach","sType":"*AS ","C":[{"N":"data","sType":"*A ","C":[{"N":"mergeAdj","C":[{"N":"varRef","sType":"*","name":"Q{}i","slot":"0","ns":"= xml=~ fn=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~ ","role":"select","line":"52"}]}]},{"N":"fn","name":"string","sType":"1AS ","C":[{"N":"dot"}]}]},{"N":"str","sType":"1AS ","val":" "}]}]},{"N":"valueOf","sType":"1NT ","C":[{"N":"str","sType":"1AS ","val":": $Duration\n"}]},{"N":"valueOf","flags":"l","sType":"1NT ","line":"53","C":[{"N":"fn","name":"string-join","role":"select","C":[{"N":"forEach","sType":"*AS ","C":[{"N":"data","sType":"*A ","C":[{"N":"mergeAdj","C":[{"N":"filter","sType":"*","flags":"p","ns":"= xml=~ fn=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~ ","role":"select","line":"53","C":[{"N":"varRef","name":"Q{}chordSeq","slot":"2"},{"N":"arith","op":"+","calc":"a+a","C":[{"N":"int","val":"1"},{"N":"arith","op":"mod","calc":"a%a","C":[{"N":"check","card":"?","diag":"1|0||arith","C":[{"N":"data","diag":"1|0||arith","C":[{"N":"varRef","name":"Q{}i","slot":"0"}]}]},{"N":"fn","name":"count","C":[{"N":"varRef","name":"Q{}chordSeq","slot":"2"}]}]}]}]}]}]},{"N":"fn","name":"string","sType":"1AS ","C":[{"N":"dot"}]}]},{"N":"str","sType":"1AS ","val":" "}]}]},{"N":"callT","bSlot":"0","sType":"* ","name":"Q{}measure","line":"54","C":[{"N":"withParam","name":"Q{}i","slot":"0","sType":"?A","C":[{"N":"arith","op":"+","calc":"a+a","sType":"?A","ns":"= xml=~ fn=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~ ","role":"select","line":"55","C":[{"N":"check","card":"?","diag":"1|0||arith","C":[{"N":"data","diag":"1|0||arith","C":[{"N":"varRef","name":"Q{}i","slot":"0"}]}]},{"N":"int","val":"1"}]}]},{"N":"withParam","name":"Q{}count","slot":"1","sType":"*","C":[{"N":"varRef","name":"Q{}count","slot":"1","sType":"*","ns":"= xml=~ fn=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~ ","role":"select","line":"56"}]},{"N":"withParam","name":"Q{}chordSeq","slot":"2","sType":"*","C":[{"N":"varRef","name":"Q{}chordSeq","slot":"2","sType":"*","ns":"= xml=~ fn=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~ ","role":"select","line":"57"}]}]}]},{"N":"true"},{"N":"empty","sType":"0 "}]}]}]}]},{"N":"co","binds":"","id":"2","vis":"PUBLIC","ex:uniform":"true","C":[{"N":"globalParam","name":"Q{}groove","sType":"* ","slots":"200","module":"groove.xsl","flags":"r","as":"","ns":"xml=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~","C":[{"N":"str","sType":"1AS ","val":"","role":"select"}]}]},{"N":"co","binds":"","id":"3","vis":"PUBLIC","ex:uniform":"true","C":[{"N":"globalParam","name":"Q{}chords","sType":"* ","slots":"200","module":"groove.xsl","as":"","ns":"xml=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~","C":[{"N":"str","val":"z","sType":"1AS","ns":"= xml=~ fn=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~ ","role":"select","line":"24"}]}]},{"N":"co","binds":"","id":"4","vis":"PUBLIC","ex:uniform":"true","C":[{"N":"globalParam","name":"Q{}tempo","sType":"* ","slots":"200","module":"groove.xsl","as":"","ns":"xml=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~","C":[{"N":"int","val":"120","sType":"1ADI","ns":"= xml=~ fn=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~ ","role":"select","line":"25"}]}]},{"N":"co","binds":"","id":"5","vis":"PUBLIC","ex:uniform":"true","C":[{"N":"globalParam","name":"Q{}count","sType":"* ","slots":"200","module":"groove.xsl","as":"","ns":"xml=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~","C":[{"N":"int","val":"4","sType":"1ADI","ns":"= xml=~ fn=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~ ","role":"select","line":"26"}]}]},{"N":"co","binds":"","id":"6","vis":"PUBLIC","ex:uniform":"true","C":[{"N":"globalParam","name":"Q{}keysig","sType":"* ","slots":"200","module":"groove.xsl","as":"","ns":"xml=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~","C":[{"N":"str","val":"C","sType":"1AS","ns":"= xml=~ fn=~ xsl=~ xs=~ mma=http://www.mellowood.ca/mma musicxml=http://www.w3.org/2021/06/musicxml40 map=~ array=~ ","role":"select","line":"27"}]}]},{"N":"co","binds":"","id":"7","C":[{"N":"mode","onNo":"TC","flags":"","patternSlots":"0","prec":""}]},{"N":"overridden"},{"N":"output","C":[{"N":"property","name":"Q{http://saxon.sf.net/}stylesheet-version","value":"30"},{"N":"property","name":"method","value":"text"},{"N":"property","name":"media-type","value":"text/plain"},{"N":"property","name":"omit-xml-declaration","value":"yes"}]},{"N":"decimalFormat"}],"Σ":"d33985c9"}
2 changes: 1 addition & 1 deletion build/mma.sef.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/musicxml.sef.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/timemap.sef.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/unroll.sef.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "musicxml-midi",
"version": "2.5.2",
"version": "2.6.0",
"description": "MusicXML to MIDI converter",
"type": "module",
"directories": {
Expand All @@ -19,11 +19,10 @@
"convert:unroll": "run() { xslt3 -xsl:src/xsl/unroll.xsl -s:\"$1\" ${@:2}; }; run",
"convert:timemap": "run() { xslt3 -xsl:src/xsl/timemap.xsl -s:\"$1\" ${@:2}; }; run",
"convert:mma": "run() { xslt3 -xsl:src/xsl/mma.xsl -s:\"$1\" ${@:2}; }; run",
"convert:groove": "run() { xslt3 -it:groove -xsl:src/xsl/groove.xsl groove=\"$1\" ${@:2}; }; run",
"convert:midi": "run() { ${MMA_HOME:-mma}/mma.py -II \"$1\" -f \"${1/.mma/.mid}\"; }; run",
"convert": "run() { mma=$(xslt3 -xsl:build/mma.sef.json -s:\"$1\" useSef=1 ${@:2}); echo \"$mma\" | ${MMA_HOME:-mma}/mma.py -II -f \"${1/.musicxml/.mid}\" -; }; run",
"debug:chord": "run() { echo \"PrintChord $1\" | ${MMA_HOME:-mma}/mma.py -n -; }; run",
"debug:grooves": "find ${MMA_HOME:-mma}/lib grooves -name '*.mma' | while read f; do MMA_ENCODING=utf-8 ${MMA_HOME:-mma}/mma.py -Dbo \"$f\" | tail -n +2; done",
"debug:preview": "run() { ${MMA_HOME:-mma}/mma.py -V \"$@\"; }; run",
"debug:musicxml": "run() { xslt3 -xsl:src/xsl/musicxml.xsl -s:\"$1\" ${@:2}; }; run",
"validate:musicxml": "run() { xmllint --noout --schema src/xsd/musicxml.xsd \"$1\"; }; run",
"validate:mma": "run() { ${MMA_HOME:-mma}/mma.py -II -n \"$1\"; }; run",
Expand Down
33 changes: 13 additions & 20 deletions src/js/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,27 +171,9 @@ app.post('/groove', async (req, res, next) => {
})
}

// Generate MMA.
const chords = params['chords'].split(',').map(s => s.trim())
const measures = [...Array(parseInt(params['count']))].map((_, index) => {
return `
MidiMark 0 Measure:${index}: $Duration
${chords[index % chords.length]}
`.trim()
}).join('\n')
const mma = `
MidiText Generated by musicxml-midi converter github.com/infojunkie/musicxml-midi
KeySig ${params['keysig']}
Tempo ${params['tempo']}
Groove ${params['groove']}
MidiMark Groove:${params['groove']}
Set Duration $( round( $_Time * 60000 / $_Tempo ) )
${measures}
`.trim()

// Check first in cache.
const hash = crypto.createHash('sha256')
hash.update(mma)
hash.update(JSON.stringify(params))
const sig = hash.digest('hex')
const cacheFile = path.resolve(path.join(process.env.CACHE_DIR || 'cache', `${sig}.mid`))
try {
Expand All @@ -205,8 +187,19 @@ ${measures}
}

try {
console.info(`[SaxonJS] Generating groove '${params['groove']}'...`)
const mma = await SaxonJS.transform({
stylesheetFileName: 'build/groove.sef.json',
destination: 'serialized',
stylesheetParams: params,
initialTemplate: 'groove',
}, 'async')
.catch(AbortChainError.chain(error => {
console.error(`[SaxonJS] ${error.code}: ${error.message}`)
res.status(400).send(ERROR_BAD_PARAM)
}))
const execResult = await exec('echo "$mma" | ${MMA_HOME:-mma}/mma.py -II -f "$out" -', {
env: { ...process.env, 'mma': mma, 'out': cacheFile }
env: { ...process.env, 'mma': mma.principalResult, 'out': cacheFile }
})
.catch(AbortChainError.chain(error => {
console.error(`[MMA] ${error.stdout.replace(/^\s+|\s+$/g, '')}`)
Expand Down
62 changes: 62 additions & 0 deletions src/xsl/groove.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>

<!--
Generate a Musical MIDI Accompaniment (MMA) script for a given groove.
-->

<xsl:stylesheet
version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mma="http://www.mellowood.ca/mma"
xmlns:musicxml="http://www.w3.org/2021/06/musicxml40"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
exclude-result-prefixes="#all"
>

<xsl:output method="text" media-type="text/plain" omit-xml-declaration="yes"/>

<!--
User-defined arguments.
-->
<xsl:param name="groove" required="yes"/>
<xsl:param name="chords" select="'z'"/>
<xsl:param name="tempo" select="120"/>
<xsl:param name="count" select="4"/>
<xsl:param name="keysig" select="'C'"/>

<xsl:template name="groove">
<xsl:text>
MidiText Generated by github.com/infojunkie/musicxml-midi
KeySig </xsl:text><xsl:value-of select="$keysig"/><xsl:text>
Tempo </xsl:text><xsl:value-of select="$tempo"/><xsl:text>
Groove </xsl:text><xsl:value-of select="$groove"/><xsl:text>
MidiMark Groove:</xsl:text><xsl:value-of select="$groove"/><xsl:text>
Set Duration $( round( $_Time * 60000 / $_Tempo ) )
</xsl:text>
<xsl:variable name="chordSeq" select="fn:tokenize($chords, '\s*,\s*')"/>
<xsl:call-template name="measure">
<xsl:with-param name="i" select="0"/>
<xsl:with-param name="count" select="$count"/>
<xsl:with-param name="chordSeq" select="$chordSeq"/>
</xsl:call-template>
</xsl:template>

<xsl:template name="measure">
<xsl:param name="i"/>
<xsl:param name="count"/>
<xsl:param name="chordSeq"/>
<xsl:if test="$i &lt; $count">
<xsl:text>
MidiMark 0 Measure:</xsl:text><xsl:value-of select="$i"/><xsl:text>: $Duration
</xsl:text><xsl:value-of select="$chordSeq[1 + ($i mod count($chordSeq))]"/>
<xsl:call-template name="measure">
<xsl:with-param name="i" select="$i+1"/>
<xsl:with-param name="count" select="$count"/>
<xsl:with-param name="chordSeq" select="$chordSeq"/>
</xsl:call-template>
</xsl:if>
</xsl:template>

</xsl:stylesheet>
2 changes: 1 addition & 1 deletion src/xsl/mma.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@
-->
<xsl:template match="score-partwise">
<xsl:text>
MidiText Generated by musicxml-midi converter https://github.com/infojunkie/musicxml-midi
MidiText Generated by github.com/infojunkie/musicxml-midi

Begin Chord-Custom
Voice </xsl:text><xsl:value-of select="$chordInstrument"/><xsl:text>
Expand Down
14 changes: 7 additions & 7 deletions test/02-unroll.bats
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set -euo pipefail

@test "unroll produces a valid file for repeats" {
unroll=$(xslt3 -xsl:src/xsl/unroll.xsl -s:test/data/repeats.musicxml)
echo "${unroll}" | xmllint --schema src/xsd/musicxml.xsd --nonet --noout -
echo "$unroll" | xmllint --schema src/xsd/musicxml.xsd --nonet --noout -
}

teardown() {
Expand All @@ -24,18 +24,18 @@ teardown() {

@test "unroll maintains implicit state" {
unroll=$(xslt3 -xsl:src/xsl/unroll.xsl -s:test/data/blue-bag-folly.musicxml)
echo "${unroll}" | xmllint --schema src/xsd/musicxml.xsd --nonet --noout -
run $(echo "${unroll}" | xmllint --xpath "count(//measure[@number='11']//sound[@tempo])" -)
echo "$unroll" | xmllint --schema src/xsd/musicxml.xsd --nonet --noout -
run $(echo "$unroll" | xmllint --xpath "count(//measure[@number='11']//sound[@tempo])" -)
assert_output --partial '1'
}

@test "unroll with renumbering produces a valid file" {
unroll=$(xslt3 -xsl:src/xsl/unroll.xsl -s:test/data/salma-ya-salama.musicxml "renumberMeasures=1")
echo "${unroll}" | xmllint --schema src/xsd/musicxml.xsd --nonet --noout -
run $(echo "${unroll}" | xmllint --xpath "count(//measure[@number='1'])" -)
echo "$unroll" | xmllint --schema src/xsd/musicxml.xsd --nonet --noout -
run $(echo "$unroll" | xmllint --xpath "count(//measure[@number='1'])" -)
assert_output --partial '1'
unroll=$(xslt3 -xsl:src/xsl/unroll.xsl -s:test/data/salma-ya-salama.musicxml "renumberMeasures=0")
echo "${unroll}" | xmllint --schema src/xsd/musicxml.xsd --nonet --noout -
run $(echo "${unroll}" | xmllint --xpath "count(//measure[@number='1'])" -)
echo "$unroll" | xmllint --schema src/xsd/musicxml.xsd --nonet --noout -
run $(echo "$unroll" | xmllint --xpath "count(//measure[@number='1'])" -)
assert_output --partial '2'
}
Loading

0 comments on commit 2360f9d

Please sign in to comment.