Skip to content

Commit

Permalink
Support duration in timemap
Browse files Browse the repository at this point in the history
  • Loading branch information
infojunkie committed Jul 18, 2024
1 parent ec2a9d4 commit 70e67af
Show file tree
Hide file tree
Showing 14 changed files with 27 additions and 11 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ This converter aims to create a valid MMA accompaniment script out of a MusicXML
## Output metadata in the MIDI file
The produced MMA script / MIDI file contains metadata that can be useful to downstream consumers. This metadata is generally expressed as [MIDI Marker meta messages](https://www.recordingblogs.com/wiki/midi-marker-meta-message), with the following syntax:
- `Measure:N` informs the consumer that the MIDI playback has reached measure N (0-based) in the score.
- `Duration:T` informs the consumer of the duration of the current measure in milliseconds.
- `Groove:X` informs the consumer that the MIDI playback is henceforth using the specified playback style.
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.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "musicxml-midi",
"version": "2.4.0",
"version": "2.5.0",
"description": "MusicXML to MIDI converter",
"type": "module",
"directories": {
Expand Down
4 changes: 3 additions & 1 deletion src/js/midi-timemap.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@ midi.tracks[0].forEach((event) => {
}) === 0
) {
const measure = Number(marker[1])
const duration = Number(marker[2])
const timestamp = Math.round(offset * (microsecondsPerQuarter / midi.header.ticksPerBeat)) / 1000
timemap.push({
measure,
timestamp
timestamp,
duration
})
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/xsl/mma.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,13 @@ MidiMark Groove:<xsl:value-of select="$groove"/>
Measure number.
-->
<xsl:text>&#xa;</xsl:text>
<xsl:text>MidiMark Measure:</xsl:text><xsl:value-of select="accumulator-after('measureIndex')(@number)"/>
<xsl:text>MidiMark Measure:</xsl:text><xsl:value-of select="accumulator-after('measureIndex')(@number)"/>:<xsl:value-of select="
musicxml:timeToMillisecs(
accumulator-after('measureDuration'),
accumulator-after('divisions'),
accumulator-after('tempo')
)
"/>
<xsl:text>&#xa;</xsl:text>

<!--
Expand Down
5 changes: 5 additions & 0 deletions src/xsl/timemap.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@
accumulator-before('measureOnset'),
accumulator-after('divisions'),
accumulator-after('tempo')
),
'duration': musicxml:timeToMillisecs(
accumulator-after('measureDuration'),
accumulator-after('divisions'),
accumulator-after('tempo')
)
}"/>
</xsl:template>
Expand Down
4 changes: 2 additions & 2 deletions test/03-mma.bats
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ set -euo pipefail
mma=$(xslt3 -xsl:src/xsl/mma.xsl -s:test/data/repeats.musicxml)
echo $mma | ${MMA_HOME:-mma}/mma.py -II -n -
run echo $mma
assert_output --partial 'KeySig 0 Time 4 TimeSig 4/4 MidiMark Measure:0 Solo Riff 768tr; z MidiMark Measure:1 Solo Riff 768tr; z KeySig 0 Time 4 TimeSig 4/4 MidiMark Measure:0 Solo Riff 768tr; z MidiMark Measure:1 Solo Riff 768tr; z KeySig 0 Time 4 TimeSig 4/4 MidiMark Measure:0 Solo Riff 768tr; z MidiMark Measure:1 Solo Riff 768tr; z MidiMark Measure:2 Solo Riff 768tr; z MidiMark Measure:3 Solo Riff 768tr; z MidiMark Measure:2 Solo Riff 768tr; z MidiMark Measure:3 Solo Riff 768tr; z MidiMark Measure:2 Solo Riff 768tr; z MidiMark Measure:4 Solo Riff 768tr; z MidiMark Measure:5 Solo Riff 768tr; z MidiMark Measure:2 Solo Riff 768tr; z MidiMark Measure:4 Solo Riff 768tr; z MidiMark Measure:5 Solo Riff 768tr; z MidiMark Measure:2 Solo Riff 768tr; z MidiMark Measure:4 Solo Riff 768tr; z MidiMark Measure:5 Solo Riff 768tr; z MidiMark Measure:2 Solo Riff 768tr; z MidiMark Measure:4 Solo Riff 768tr; z MidiMark Measure:5 Solo Riff 768tr; z MidiMark Measure:6 Solo Riff 768tr; z MidiMark Measure:7 Solo Riff 768tr; z MidiMark Measure:8 Solo Riff 768tr; z MidiMark Measure:7 Solo Riff 768tr; z MidiMark Measure:9 Solo Riff 768tr; z MidiMark Measure:7 Solo Riff 768tr; z MidiMark Measure:8 Solo Riff 768tr; z MidiMark Measure:7 Solo Riff 768tr; z MidiMark Measure:9 Solo Riff 768tr; z MidiMark Measure:7 Solo Riff 768tr; z MidiMark Measure:8 Solo Riff 768tr; z MidiMark Measure:7 Solo Riff 768tr; z MidiMark Measure:9 Solo Riff 768tr; z MidiMark Measure:7 Solo Riff 768tr; z MidiMark Measure:8 Solo Riff 768tr; z MidiMark Measure:7 Solo Riff 768tr; z MidiMark Measure:10 Solo Riff 768tr; z MidiMark Measure:11 Solo Riff 768tr; z KeySig 0 Time 4 TimeSig 4/4 MidiMark Measure:0 Solo Riff 768tr; z MidiMark Measure:1 Solo Riff 768tr; z MidiMark Measure:2 Solo Riff 768tr; z MidiMark Measure:3 Solo Riff 768tr; z MidiMark Measure:12 Solo Riff 768tr; z MidiMark Measure:13 Solo Riff 768tr; z MidiMark Measure:6 Solo Riff 768tr; z MidiMark Measure:7 Solo Riff 768tr; z'
assert_output --partial 'KeySig 0 Time 4 TimeSig 4/4 MidiMark Measure:0:2000 Solo Riff 768tr; z MidiMark Measure:1:2000 Solo Riff 768tr; z KeySig 0 Time 4 TimeSig 4/4 MidiMark Measure:0:2000 Solo Riff 768tr; z MidiMark Measure:1:2000 Solo Riff 768tr; z KeySig 0 Time 4 TimeSig 4/4 MidiMark Measure:0:2000 Solo Riff 768tr; z MidiMark Measure:1:2000 Solo Riff 768tr; z MidiMark Measure:2:2000 Solo Riff 768tr; z MidiMark Measure:3:2000 Solo Riff 768tr; z MidiMark Measure:2:2000 Solo Riff 768tr; z MidiMark Measure:3:2000 Solo Riff 768tr; z MidiMark Measure:2:2000 Solo Riff 768tr; z MidiMark Measure:4:2000 Solo Riff 768tr; z MidiMark Measure:5:2000 Solo Riff 768tr; z MidiMark Measure:2:2000 Solo Riff 768tr; z MidiMark Measure:4:2000 Solo Riff 768tr; z MidiMark Measure:5:2000 Solo Riff 768tr; z MidiMark Measure:2:2000 Solo Riff 768tr; z MidiMark Measure:4:2000 Solo Riff 768tr; z MidiMark Measure:5:2000 Solo Riff 768tr; z MidiMark Measure:2:2000 Solo Riff 768tr; z MidiMark Measure:4:2000 Solo Riff 768tr; z MidiMark Measure:5:2000 Solo Riff 768tr; z MidiMark Measure:6:2000 Solo Riff 768tr; z MidiMark Measure:7:2000 Solo Riff 768tr; z MidiMark Measure:8:2000 Solo Riff 768tr; z MidiMark Measure:7:2000 Solo Riff 768tr; z MidiMark Measure:9:2000 Solo Riff 768tr; z MidiMark Measure:7:2000 Solo Riff 768tr; z MidiMark Measure:8:2000 Solo Riff 768tr; z MidiMark Measure:7:2000 Solo Riff 768tr; z MidiMark Measure:9:2000 Solo Riff 768tr; z MidiMark Measure:7:2000 Solo Riff 768tr; z MidiMark Measure:8:2000 Solo Riff 768tr; z MidiMark Measure:7:2000 Solo Riff 768tr; z MidiMark Measure:9:2000 Solo Riff 768tr; z MidiMark Measure:7:2000 Solo Riff 768tr; z MidiMark Measure:8:2000 Solo Riff 768tr; z MidiMark Measure:7:2000 Solo Riff 768tr; z MidiMark Measure:10:2000 Solo Riff 768tr; z MidiMark Measure:11:2000 Solo Riff 768tr; z KeySig 0 Time 4 TimeSig 4/4 MidiMark Measure:0:2000 Solo Riff 768tr; z MidiMark Measure:1:2000 Solo Riff 768tr; z MidiMark Measure:2:2000 Solo Riff 768tr; z MidiMark Measure:3:2000 Solo Riff 768tr; z MidiMark Measure:12:2000 Solo Riff 768tr; z MidiMark Measure:13:2000 Solo Riff 768tr; z MidiMark Measure:6:2000 Solo Riff 768tr; z MidiMark Measure:7:2000 Solo Riff 768tr; z'
}

@test "mma produces a valid file for chords" {
Expand All @@ -72,7 +72,7 @@ set -euo pipefail
mma=$(xslt3 -xsl:src/xsl/mma.xsl -s:test/data/ties.musicxml)
echo $mma | ${MMA_HOME:-mma}/mma.py -II -n -
run echo $mma
assert_output --partial 'KeySig 0 Time 4 TimeSig 4/4 MidiMark Measure:0 Solo Riff 192tcn+;192tdn+;384ten+; z MidiMark Measure:1 Solo Riff 336tcn+;48tr;384tdn+; z MidiMark Measure:2 Solo Riff 192tcn+;192tdn+;192ten+;576tfn+~; z MidiMark Measure:3 Solo Riff ~1344tcn+~; z MidiMark Measure:4 Solo Riff ~<>~; z MidiMark Measure:5 Solo Riff ~768tcn+,en+,gn+~; z MidiMark Measure:6 Solo Riff ~576tcn+,en+,gn+; z MidiMark Measure:7 Solo Riff 192tfn,an,dn+;192tan,dn+,fn+;192tan,cn+,en+;192tfn,an,dn+; z'
assert_output --partial 'KeySig 0 Time 4 TimeSig 4/4 MidiMark Measure:0:2000 Solo Riff 192tcn+;192tdn+;384ten+; z MidiMark Measure:1:2000 Solo Riff 336tcn+;48tr;384tdn+; z MidiMark Measure:2:2000 Solo Riff 192tcn+;192tdn+;192ten+;576tfn+~; z MidiMark Measure:3:2000 Solo Riff ~1344tcn+~; z MidiMark Measure:4:2000 Solo Riff ~<>~; z MidiMark Measure:5:2000 Solo Riff ~768tcn+,en+,gn+~; z MidiMark Measure:6:2000 Solo Riff ~576tcn+,en+,gn+; z MidiMark Measure:7:2000 Solo Riff 192tfn,an,dn+;192tan,dn+,fn+;192tan,cn+,en+;192tfn,an,dn+; z'
}

@test "mma produces a valid and correct file for key-signatures" {
Expand Down
1 change: 1 addition & 0 deletions test/04-timemap.bats
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ set -euo pipefail
select=$(echo "${timemap}" | jq '.[] | select(.measure == 1)')
run echo ${select}
assert_output --partial '"timestamp": 500'
assert_output --partial '"duration": 1000'
}
Binary file modified test/data/midi-timemap.test.mid
Binary file not shown.
1 change: 1 addition & 0 deletions test/midi-timemap.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ describe('MIDI to timemap conversion cli', () => {
const execResult = await exec('node src/js/midi-timemap test/data/midi-timemap.test.mid')
const timemap = JSON.parse(execResult.stdout);
expect(timemap[timemap.length-1].timestamp).toEqual(116800)
expect(timemap[timemap.length-1].duration).toEqual(1600)
})
})

0 comments on commit 70e67af

Please sign in to comment.