From 70828998bf3f9e0b12dd5240f8aa628318490bcf Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 14 Dec 2025 21:07:21 +0100 Subject: [PATCH 1/2] test: avoid mocha trying to visualize cyclic objects --- .../test/audio/MidiFileGenerator.test.ts | 2 +- .../test/exporter/Gp7Exporter.test.ts | 2 +- .../test/importer/AlphaTexImporter.test.ts | 83 ++++++++++++++----- .../test/importer/AlphaTexImporterOld.test.ts | 44 +++++----- .../test/importer/Gp8Importer.test.ts | 4 +- .../test/importer/GpImporterTestHelper.ts | 2 +- .../test/importer/MusicXmlImporter.test.ts | 4 +- 7 files changed, 90 insertions(+), 51 deletions(-) diff --git a/packages/alphatab/test/audio/MidiFileGenerator.test.ts b/packages/alphatab/test/audio/MidiFileGenerator.test.ts index 15afb5d08..11323b6f7 100644 --- a/packages/alphatab/test/audio/MidiFileGenerator.test.ts +++ b/packages/alphatab/test/audio/MidiFileGenerator.test.ts @@ -1252,7 +1252,7 @@ describe('MidiFileGeneratorTest', () => { const score: Score = parseTex(tex); expect(score.tempo).to.be.equal(60); - expect(score.masterBars[0].tempoAutomations).to.have.length(1); + expect(score.masterBars[0].tempoAutomations.length).to.equal(1); expect(score.masterBars[0].tempoAutomations[0]!.value).to.be.equal(60); const handler: FlatMidiEventGenerator = new FlatMidiEventGenerator(); diff --git a/packages/alphatab/test/exporter/Gp7Exporter.test.ts b/packages/alphatab/test/exporter/Gp7Exporter.test.ts index 1086aefda..32a675580 100644 --- a/packages/alphatab/test/exporter/Gp7Exporter.test.ts +++ b/packages/alphatab/test/exporter/Gp7Exporter.test.ts @@ -159,7 +159,7 @@ describe('Gp7ExporterTest', () => { ComparisonHelpers.expectJsonEqual(expectedJson, actualJson, '', ['accidentalmode']); - expect(actual.tracks[0].percussionArticulations).to.have.length(2); + expect(actual.tracks[0].percussionArticulations.length).to.equal(2); expect(actual.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].percussionArticulation).to.equal(0); expect(actual.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].percussionArticulation).to.equal(1); expect(actual.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].percussionArticulation).to.equal(0); diff --git a/packages/alphatab/test/importer/AlphaTexImporter.test.ts b/packages/alphatab/test/importer/AlphaTexImporter.test.ts index 939e02f79..695e1429f 100644 --- a/packages/alphatab/test/importer/AlphaTexImporter.test.ts +++ b/packages/alphatab/test/importer/AlphaTexImporter.test.ts @@ -1422,7 +1422,7 @@ describe('AlphaTexImporterTest', () => { const score = parseTex(` . \\tempo 120 1.1.4 1.1 1.1{tempo 60} 1.1 | 1.1.4{tempo 100} 1.1 1.1{tempo 120} 1.1 `); - expect(score.masterBars[0].tempoAutomations).to.have.length(2); + expect(score.masterBars[0].tempoAutomations.length).to.equal(2); expect(score.masterBars[0].tempoAutomations[0].value).to.equal(120); expect(score.masterBars[0].tempoAutomations[0].ratioPosition).to.equal(0); expect(score.masterBars[0].tempoAutomations[1].value).to.equal(60); @@ -1573,7 +1573,7 @@ describe('AlphaTexImporterTest', () => { c3 d3 e3 f3 | c3 d3 e3 f3 `); - expect(score.masterBars).to.have.length(2); + expect(score.masterBars.length).to.equal(2); expect(score.tracks[0].staves[0].bars.length).to.equal(2); expect(score.tracks[0].staves[0].bars[0].voices.length).to.equal(2); @@ -1589,11 +1589,11 @@ describe('AlphaTexImporterTest', () => { c3 d3 e3 f3 | c3 d3 e3 f3 `); - expect(score.masterBars).to.have.length(2); + expect(score.masterBars.length).to.equal(2); - expect(score.tracks[0].staves[0].bars).to.have.length(2); - expect(score.tracks[0].staves[0].bars[0].voices).to.have.length(2); - expect(score.tracks[0].staves[0].bars[1].voices).to.have.length(2); + expect(score.tracks[0].staves[0].bars.length).to.equal(2); + expect(score.tracks[0].staves[0].bars[0].voices.length).to.equal(2); + expect(score.tracks[0].staves[0].bars[1].voices.length).to.equal(2); testExportRoundtrip(score); }); @@ -1604,11 +1604,11 @@ describe('AlphaTexImporterTest', () => { c3 d3 e3 f3 | c3 d3 e3 f3 `); - expect(score.masterBars).to.have.length(2); + expect(score.masterBars.length).to.equal(2); - expect(score.tracks[0].staves[0].bars).to.have.length(2); - expect(score.tracks[0].staves[0].bars[0].voices).to.have.length(2); - expect(score.tracks[0].staves[0].bars[1].voices).to.have.length(2); + expect(score.tracks[0].staves[0].bars.length).to.equal(2); + expect(score.tracks[0].staves[0].bars[0].voices.length).to.equal(2); + expect(score.tracks[0].staves[0].bars[1].voices.length).to.equal(2); testExportRoundtrip(score); }); @@ -1677,13 +1677,13 @@ describe('AlphaTexImporterTest', () => { 3.3.4{ tb dive gradual (0 -12.5) } | `); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarType).to.equal(WhammyType.Dive); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints).to.have.length(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints!.length).to.equal(2); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].value).to.equal(0); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].value).to.equal(-12.5); expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarType).to.equal(WhammyType.Dive); expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyStyle).to.equal(BendStyle.Gradual); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints).to.have.length(2); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints!.length).to.equal(2); expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].value).to.equal(0); expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].value).to.equal(-12.5); testExportRoundtrip(score); @@ -1722,7 +1722,7 @@ describe('AlphaTexImporterTest', () => { G4 G4 G4 { instrument brightacousticpiano } `); expect(score.tracks[0].playbackInfo.program).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations).to.have.length(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations.length).to.equal(1); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].type).to.equal( AutomationType.Instrument ); @@ -1746,7 +1746,7 @@ describe('AlphaTexImporterTest', () => { `); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendType).to.equal(BendType.Bend); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendStyle).to.equal(BendStyle.Gradual); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints).to.have.length(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints!.length).to.equal(2); testExportRoundtrip(score); }); @@ -1834,7 +1834,7 @@ describe('AlphaTexImporterTest', () => { expect(score.tempo).to.equal(100); expect(score.tempoLabel).to.equal('T1'); - expect(score.masterBars[1].tempoAutomations).to.have.length(1); + expect(score.masterBars[1].tempoAutomations.length).to.equal(1); expect(score.masterBars[1].tempoAutomations[0].value).to.equal(80); expect(score.masterBars[1].tempoAutomations[0].text).to.equal('T2'); testExportRoundtrip(score); @@ -1864,7 +1864,7 @@ describe('AlphaTexImporterTest', () => { `); expect(score.defaultSystemsLayout).to.equal(5); - expect(score.systemsLayout).to.have.length(3); + expect(score.systemsLayout.length).to.equal(3); expect(score.systemsLayout[0]).to.equal(3); expect(score.systemsLayout[1]).to.equal(2); expect(score.systemsLayout[2]).to.equal(3); @@ -1905,7 +1905,7 @@ describe('AlphaTexImporterTest', () => { expect(score.tracks[0].color.rgba).to.equal('#FF0000'); expect(score.tracks[0].defaultSystemsLayout).to.equal(6); - expect(score.tracks[0].systemsLayout).to.have.length(3); + expect(score.tracks[0].systemsLayout.length).to.equal(3); expect(score.tracks[0].systemsLayout[0]).to.equal(3); expect(score.tracks[0].systemsLayout[1]).to.equal(2); expect(score.tracks[0].systemsLayout[0]).to.equal(3); @@ -2218,13 +2218,13 @@ describe('AlphaTexImporterTest', () => { expect(score.tracks[0].playbackInfo.volume).to.equal(7); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations).to.have.length(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations.length).to.equal(1); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].type).to.equal( AutomationType.Volume ); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].value).to.equal(8); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations).to.have.length(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations.length).to.equal(1); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].type).to.equal( AutomationType.Volume ); @@ -2242,13 +2242,13 @@ describe('AlphaTexImporterTest', () => { expect(score.tracks[0].playbackInfo.balance).to.equal(7); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations).to.have.length(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations.length).to.equal(1); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].type).to.equal( AutomationType.Balance ); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].value).to.equal(8); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations).to.have.length(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations.length).to.equal(1); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].type).to.equal( AutomationType.Balance ); @@ -2272,7 +2272,7 @@ describe('AlphaTexImporterTest', () => { `); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].deadSlapped).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes).to.have.length(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).to.equal(0); testExportRoundtrip(score); }); @@ -2490,4 +2490,43 @@ describe('AlphaTexImporterTest', () => { testExportRoundtrip(score); }); + + describe('voice-mode', () => { + it('default', () => { + expect( + parseTex(` + \\voice + C4 | C5 + \\voice + C3 | C4 + `) + ).toMatchSnapshot(); + }); + it('staffWise', () => { + expect( + parseTex(` + \\voiceMode staffWise + \\voice + C4 | C5 + \\voice + C3 | C4 + `) + ).toMatchSnapshot(); + }); + it('barWise', () => { + + expect( + parseTex(` + \\voiceMode barWise + // Bar 1 + \\voice C4 + \\voice C3 + | + // Bar 2 + \\voice C5 + \\voice C4 + `) + ).toMatchSnapshot(); + }); + }); }); diff --git a/packages/alphatab/test/importer/AlphaTexImporterOld.test.ts b/packages/alphatab/test/importer/AlphaTexImporterOld.test.ts index d9d7ec042..92455e4b0 100644 --- a/packages/alphatab/test/importer/AlphaTexImporterOld.test.ts +++ b/packages/alphatab/test/importer/AlphaTexImporterOld.test.ts @@ -1409,7 +1409,7 @@ describe('AlphaTexImporterOldTest', () => { const score = parseTex(` . \\tempo 120 1.1.4 1.1 1.1{tempo 60} 1.1 | 1.1.4{tempo 100} 1.1 1.1{tempo 120} 1.1 `); - expect(score.masterBars[0].tempoAutomations).to.have.length(2); + expect(score.masterBars[0].tempoAutomations.length).to.equal(2); expect(score.masterBars[0].tempoAutomations[0].value).to.equal(120); expect(score.masterBars[0].tempoAutomations[0].ratioPosition).to.equal(0); expect(score.masterBars[0].tempoAutomations[1].value).to.equal(60); @@ -1560,7 +1560,7 @@ describe('AlphaTexImporterOldTest', () => { c3 d3 e3 f3 | c3 d3 e3 f3 `); - expect(score.masterBars).to.have.length(2); + expect(score.masterBars.length).to.equal(2); expect(score.tracks[0].staves[0].bars.length).to.equal(2); expect(score.tracks[0].staves[0].bars[0].voices.length).to.equal(2); @@ -1576,11 +1576,11 @@ describe('AlphaTexImporterOldTest', () => { c3 d3 e3 f3 | c3 d3 e3 f3 `); - expect(score.masterBars).to.have.length(2); + expect(score.masterBars.length).to.equal(2); - expect(score.tracks[0].staves[0].bars).to.have.length(2); - expect(score.tracks[0].staves[0].bars[0].voices).to.have.length(2); - expect(score.tracks[0].staves[0].bars[1].voices).to.have.length(2); + expect(score.tracks[0].staves[0].bars.length).to.equal(2); + expect(score.tracks[0].staves[0].bars[0].voices.length).to.equal(2); + expect(score.tracks[0].staves[0].bars[1].voices.length).to.equal(2); testExportRoundtrip(score); }); @@ -1591,11 +1591,11 @@ describe('AlphaTexImporterOldTest', () => { c3 d3 e3 f3 | c3 d3 e3 f3 `); - expect(score.masterBars).to.have.length(2); + expect(score.masterBars.length).to.equal(2); - expect(score.tracks[0].staves[0].bars).to.have.length(2); - expect(score.tracks[0].staves[0].bars[0].voices).to.have.length(2); - expect(score.tracks[0].staves[0].bars[1].voices).to.have.length(2); + expect(score.tracks[0].staves[0].bars.length).to.equal(2); + expect(score.tracks[0].staves[0].bars[0].voices.length).to.equal(2); + expect(score.tracks[0].staves[0].bars[1].voices.length).to.equal(2); testExportRoundtrip(score); }); @@ -1664,13 +1664,13 @@ describe('AlphaTexImporterOldTest', () => { 3.3.4{ tb dive gradual (0 -12.5) } | `); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarType).to.equal(WhammyType.Dive); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints).to.have.length(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints!.length).to.equal(2); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![0].value).to.equal(0); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints![1].value).to.equal(-12.5); expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarType).to.equal(WhammyType.Dive); expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyStyle).to.equal(BendStyle.Gradual); - expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints).to.have.length(2); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints!.length).to.equal(2); expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![0].value).to.equal(0); expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints![1].value).to.equal(-12.5); testExportRoundtrip(score); @@ -1709,7 +1709,7 @@ describe('AlphaTexImporterOldTest', () => { G4 G4 G4 { instrument brightacousticpiano } `); expect(score.tracks[0].playbackInfo.program).to.equal(0); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations).to.have.length(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations.length).to.equal(1); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].type).to.equal( AutomationType.Instrument ); @@ -1733,7 +1733,7 @@ describe('AlphaTexImporterOldTest', () => { `); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendType).to.equal(BendType.Bend); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendStyle).to.equal(BendStyle.Gradual); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints).to.have.length(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints!.length).to.equal(2); testExportRoundtrip(score); }); @@ -1821,7 +1821,7 @@ describe('AlphaTexImporterOldTest', () => { expect(score.tempo).to.equal(100); expect(score.tempoLabel).to.equal('T1'); - expect(score.masterBars[1].tempoAutomations).to.have.length(1); + expect(score.masterBars[1].tempoAutomations.length).to.equal(1); expect(score.masterBars[1].tempoAutomations[0].value).to.equal(80); expect(score.masterBars[1].tempoAutomations[0].text).to.equal('T2'); testExportRoundtrip(score); @@ -1851,7 +1851,7 @@ describe('AlphaTexImporterOldTest', () => { `); expect(score.defaultSystemsLayout).to.equal(5); - expect(score.systemsLayout).to.have.length(3); + expect(score.systemsLayout.length).to.equal(3); expect(score.systemsLayout[0]).to.equal(3); expect(score.systemsLayout[1]).to.equal(2); expect(score.systemsLayout[2]).to.equal(3); @@ -1892,7 +1892,7 @@ describe('AlphaTexImporterOldTest', () => { expect(score.tracks[0].color.rgba).to.equal('#FF0000'); expect(score.tracks[0].defaultSystemsLayout).to.equal(6); - expect(score.tracks[0].systemsLayout).to.have.length(3); + expect(score.tracks[0].systemsLayout.length).to.equal(3); expect(score.tracks[0].systemsLayout[0]).to.equal(3); expect(score.tracks[0].systemsLayout[1]).to.equal(2); expect(score.tracks[0].systemsLayout[0]).to.equal(3); @@ -2205,13 +2205,13 @@ describe('AlphaTexImporterOldTest', () => { expect(score.tracks[0].playbackInfo.volume).to.equal(7); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations).to.have.length(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations.length).to.equal(1); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].type).to.equal( AutomationType.Volume ); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].value).to.equal(8); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations).to.have.length(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations.length).to.equal(1); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].type).to.equal( AutomationType.Volume ); @@ -2229,13 +2229,13 @@ describe('AlphaTexImporterOldTest', () => { expect(score.tracks[0].playbackInfo.balance).to.equal(7); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations).to.have.length(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations.length).to.equal(1); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].type).to.equal( AutomationType.Balance ); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].automations[0].value).to.equal(8); - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations).to.have.length(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations.length).to.equal(1); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].automations[0].type).to.equal( AutomationType.Balance ); @@ -2259,7 +2259,7 @@ describe('AlphaTexImporterOldTest', () => { `); expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].deadSlapped).to.be.true; - expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes).to.have.length(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).to.equal(0); testExportRoundtrip(score); }); diff --git a/packages/alphatab/test/importer/Gp8Importer.test.ts b/packages/alphatab/test/importer/Gp8Importer.test.ts index c3e557414..1e12472f9 100644 --- a/packages/alphatab/test/importer/Gp8Importer.test.ts +++ b/packages/alphatab/test/importer/Gp8Importer.test.ts @@ -46,13 +46,13 @@ describe('Gp8ImporterTest', () => { it('beat-tempo-change', async () => { const score = (await prepareImporterWithFile('guitarpro8/beat-tempo-change.gp')).readScore(); - expect(score.masterBars[0].tempoAutomations).to.have.length(2); + expect(score.masterBars[0].tempoAutomations.length).to.equal(2); expect(score.masterBars[0].tempoAutomations[0].value).to.have.equal(120); expect(score.masterBars[0].tempoAutomations[0].ratioPosition).to.equal(0); expect(score.masterBars[0].tempoAutomations[1].value).to.equal(60); expect(score.masterBars[0].tempoAutomations[1].ratioPosition).to.equal(0.5); - expect(score.masterBars[1].tempoAutomations).to.have.length(2); + expect(score.masterBars[1].tempoAutomations.length).to.equal(2); expect(score.masterBars[1].tempoAutomations[0].value).to.equal(100); expect(score.masterBars[1].tempoAutomations[0].ratioPosition).to.equal(0); expect(score.masterBars[1].tempoAutomations[1].value).to.equal(120); diff --git a/packages/alphatab/test/importer/GpImporterTestHelper.ts b/packages/alphatab/test/importer/GpImporterTestHelper.ts index 33f09b42f..605fcdc92 100644 --- a/packages/alphatab/test/importer/GpImporterTestHelper.ts +++ b/packages/alphatab/test/importer/GpImporterTestHelper.ts @@ -302,7 +302,7 @@ export class GpImporterTestHelper { expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].text).to.equal('Text'); expect(score.masterBars[4].isDoubleBar).to.be.equal(true); - expect(score.masterBars[4].tempoAutomations).to.have.length(1); + expect(score.masterBars[4].tempoAutomations.length).to.equal(1); expect(score.masterBars[4].tempoAutomations[0]!.value).to.equal(120.0); if (!skipInstrumentCheck) { expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].getAutomation(AutomationType.Instrument)).to.be diff --git a/packages/alphatab/test/importer/MusicXmlImporter.test.ts b/packages/alphatab/test/importer/MusicXmlImporter.test.ts index 454ae4ef4..92e1012b2 100644 --- a/packages/alphatab/test/importer/MusicXmlImporter.test.ts +++ b/packages/alphatab/test/importer/MusicXmlImporter.test.ts @@ -45,9 +45,9 @@ describe('MusicXmlImporterTests', () => { ); expect(score.tempo).to.be.equal(60); - expect(score.masterBars[0].tempoAutomations).to.have.length(1); + expect(score.masterBars[0].tempoAutomations.length).to.equal(1); expect(score.masterBars[0].tempoAutomations[0]?.value).to.be.equal(60); - expect(score.masterBars[1].tempoAutomations).to.have.length(1); + expect(score.masterBars[1].tempoAutomations.length).to.equal(1); expect(score.masterBars[1].tempoAutomations[0].value).to.be.equal(60); }); it('tie-destination', async () => { From 695955c78b0f02e9a9f7338d60f253f12ccbd09b Mon Sep 17 00:00:00 2001 From: Danielku15 Date: Sun, 14 Dec 2025 21:35:44 +0100 Subject: [PATCH 2/2] feat(alphatex): Bar-Wise Voice Writing --- .../alphatab/src/importer/AlphaTexImporter.ts | 128 +- .../alphaTex/AlphaTex1EnumMappings.ts | 9 +- .../alphaTex/AlphaTex1LanguageDefinitions.ts | 1119 ++++------------- .../alphaTex/AlphaTex1LanguageHandler.ts | 16 + .../src/importer/alphaTex/AlphaTexShared.ts | 9 + .../alphatab/src/importer/alphaTex/_barrel.ts | 1 + .../AlphaTexImporterOldNewCompat.test.ts | 1 + .../AlphaTexImporter.test.ts.snap | 603 ++++++++- packages/alphatex/src/definitions.ts | 4 +- packages/alphatex/src/enum.ts | 13 + .../alphatex/src/metadata/bar/voiceMode.ts | 75 ++ packages/playground/alphatex-editor.ts | 2 +- 12 files changed, 1070 insertions(+), 910 deletions(-) create mode 100644 packages/alphatex/src/metadata/bar/voiceMode.ts diff --git a/packages/alphatab/src/importer/AlphaTexImporter.ts b/packages/alphatab/src/importer/AlphaTexImporter.ts index bad87f377..93e634c65 100644 --- a/packages/alphatab/src/importer/AlphaTexImporter.ts +++ b/packages/alphatab/src/importer/AlphaTexImporter.ts @@ -23,7 +23,8 @@ import { AlphaTexDiagnosticsSeverity, type IAlphaTexImporter, type IAlphaTexImporterState, - AlphaTexStaffNoteKind + AlphaTexStaffNoteKind, + AlphaTexVoiceMode } from '@coderline/alphatab/importer/alphaTex/AlphaTexShared'; import { ApplyNodeResult, @@ -166,6 +167,7 @@ class AlphaTexImportState implements IAlphaTexImporterState { public currentDynamics = DynamicValue.F; public accidentalMode = AlphaTexAccidentalMode.Explicit; + public voiceMode = AlphaTexVoiceMode.StaffWise; public currentTupletNumerator = -1; public currentTupletDenominator = -1; public scoreNode: AlphaTexScoreNode | undefined; @@ -349,18 +351,38 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter private _bars(node: AlphaTexScoreNode) { if (node.bars.length > 0) { + let previousBarCompleted = false; for (const b of node.bars) { - this._bar(b); + this._bar(b, previousBarCompleted); + + switch (this.state.voiceMode) { + case AlphaTexVoiceMode.StaffWise: + // if voices are staff-wise, we definitly have a new bar here + this._state.barIndex++; + previousBarCompleted = true; + break; + case AlphaTexVoiceMode.BarWise: + // if voices are bar-wise, the next bar might be another voice in the same bar + // (barIndex increment is handled inside _barMeta) + // if we have an explicit bar end, we can increase already + if (b.pipe) { + this._state.barIndex++; + this._state.voiceIndex = 0; + this._state.ignoredInitialVoice = false; + previousBarCompleted = true; + } + break; + } } } else { - this._newBar(this._state.currentStaff!); + this._getBar(this._state.currentStaff!); this._detectTuningForStaff(this._state.currentStaff!); this._handleTransposition(this._state.currentStaff!); } } - private _bar(node: AlphaTexBarNode) { - const bar = this._barMeta(node); + private _bar(node: AlphaTexBarNode, previousBarCompleted: boolean) { + const bar = this._barMeta(node, previousBarCompleted); this._detectTuningForStaff(this._state.currentStaff!); this._handleTransposition(this._state.currentStaff!); @@ -841,7 +863,7 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter } } - private _barMeta(node: AlphaTexBarNode): Bar { + private _barMeta(node: AlphaTexBarNode, previousBarCompleted: boolean): Bar { // it might be a bit an edge case but a valid one: // one might repeat multiple structural metadata // in one bar starting multiple tracks/staves/voices which are @@ -859,6 +881,7 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter let previousStaff = this._state.currentStaff!; let hadNewTrack = false; let hadNewStaff = false; + let hadNewVoice = false; let applyInitialBarMetaToPreviousStaff = false; const resetInitialBarMeta = () => { @@ -871,11 +894,18 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter previousStaff = this._state.currentStaff!; hadNewTrack = false; hadNewStaff = false; + hadNewVoice = false; applyInitialBarMetaToPreviousStaff = false; }; const bar: Lazy = new Lazy(() => { - const b = this._newBar(this._state.currentStaff!); + // had a \voice in this bar -> barIndex and voice were updated already + // if not, we start a new bar here + if (!hadNewVoice && !previousBarCompleted) { + this._state.barIndex++; + } + + const b = this._getBar(this._state.currentStaff!); if (initialBarMeta) { for (const initial of initialBarMeta) { this._handler.applyBarMetaData(this, b, initial); @@ -919,6 +949,9 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter // new bar needed on new structural level bar.reset(); break; + case ApplyStructuralMetaDataResult.AppliedNewVoice: + hadNewVoice = true; + break; } if (initialBarMeta) { @@ -989,15 +1022,14 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter return bar.value; } - private _newBar(staff: Staff): Bar { + private _getBar(staff: Staff): Bar { // existing bar? -> e.g. in multi-voice setups where we fill empty voices later if (this._state.barIndex < staff.bars.length) { const bar = staff.bars[this._state.barIndex]; - this._state.barIndex++; return bar; } - const voiceCount = staff.bars.length === 0 ? 1 : staff.bars[0].voices.length; + const voiceCount = staff.bars.length === 0 ? this._state.voiceIndex + 1 : staff.bars[0].voices.length; // need new bar const newBar: Bar = new Bar(); @@ -1008,7 +1040,7 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter newBar.keySignature = newBar.previousBar!.keySignature; newBar.keySignatureType = newBar.previousBar!.keySignatureType; } - this._state.barIndex++; + this._state.barIndex = newBar.index; if (newBar.index > 0) { newBar.clef = newBar.previousBar!.clef; @@ -1073,27 +1105,67 @@ export class AlphaTexImporter extends ScoreImporter implements IAlphaTexImporter } public startNewVoice() { - if ( - this._state.voiceIndex === 0 && - (this._state.currentStaff!.bars.length === 0 || - (this._state.currentStaff!.bars.length === 1 && - this._state.currentStaff!.bars[0].isEmpty && - !this._state.ignoredInitialVoice)) - ) { - // voice marker on the begining of the first voice without any bar yet? - // -> ignore + // only if we're on the first voice we might skip the initial \voice meta + let shouldIgnoreInitialVoice = this._state.voiceIndex === 0 && !this._state.ignoredInitialVoice; + + // this logic is expanded for readability + if (shouldIgnoreInitialVoice) { + // if we have no bars created yet, we stay on the initial voice + if (this._state.currentStaff!.bars.length === 0) { + shouldIgnoreInitialVoice = true; + } else { + switch (this._state.voiceMode) { + case AlphaTexVoiceMode.StaffWise: + // on staffwise voices, we can only ignore the "initial" voice if the + // first bar we have is completely empty + shouldIgnoreInitialVoice = + this._state.currentStaff!.bars.length === 1 && this._state.currentStaff!.bars[0].isEmpty; + break; + case AlphaTexVoiceMode.BarWise: + // on barwise voices, we ignore the bar count but check only the first voice of the current bar + // to find out if it is the initial empty one + if (this._state.barIndex < this._state.currentStaff!.bars.length) { + // bar exists -> check if empty + const bar = this._state.currentStaff!.bars[this._state.barIndex]; + shouldIgnoreInitialVoice = bar.voices[0].isEmpty; + } else { + // bar doesn't exist yet + shouldIgnoreInitialVoice = true; + } + break; + } + } + } + + if (shouldIgnoreInitialVoice) { this._state.ignoredInitialVoice = true; return; } - // create directly a new empty voice for all bars + + switch (this._state.voiceMode) { + case AlphaTexVoiceMode.StaffWise: + // start using the new voice (see newBar for details on matching) + this._state.voiceIndex++; + this._state.barIndex = 0; + this._state.currentTupletDenominator = -1; + this._state.currentTupletNumerator = -1; + + break; + case AlphaTexVoiceMode.BarWise: + this._state.voiceIndex++; + + this._state.currentTupletDenominator = -1; + this._state.currentTupletNumerator = -1; + break; + } + + // create all missing voices for (const b of this._state.currentStaff!.bars) { - const v = new Voice(); - b.addVoice(v); + while (b.voices.length <= this._state.voiceIndex) { + b.addVoice(new Voice()); + } } - // start using the new voice (see newBar for details on matching) - this._state.voiceIndex++; - this._state.barIndex = 0; - this._state.currentTupletDenominator = -1; - this._state.currentTupletNumerator = -1; + + // create voices } } diff --git a/packages/alphatab/src/importer/alphaTex/AlphaTex1EnumMappings.ts b/packages/alphatab/src/importer/alphaTex/AlphaTex1EnumMappings.ts index e02f88c37..d23426ed5 100644 --- a/packages/alphatab/src/importer/alphaTex/AlphaTex1EnumMappings.ts +++ b/packages/alphatab/src/importer/alphaTex/AlphaTex1EnumMappings.ts @@ -1,4 +1,4 @@ -import type { AlphaTexAccidentalMode } from '@coderline/alphatab/importer/alphaTex/AlphaTexShared'; +import type { AlphaTexAccidentalMode, AlphaTexVoiceMode } from '@coderline/alphatab/importer/alphaTex/AlphaTexShared'; import type { BarLineStyle } from '@coderline/alphatab/model/Bar'; import type { BarreShape } from '@coderline/alphatab/model/BarreShape'; import type { BendStyle } from '@coderline/alphatab/model/BendStyle'; @@ -74,6 +74,13 @@ export class AlphaTex1EnumMappings { public static readonly alphaTexAccidentalModeReversed = AlphaTex1EnumMappings._reverse( AlphaTex1EnumMappings.alphaTexAccidentalMode ); + public static readonly alphaTexVoiceMode = new Map([ + ['staffwise', 0], + ['barwise', 1] + ]); + public static readonly alphaTexVoiceModeReversed = AlphaTex1EnumMappings._reverse( + AlphaTex1EnumMappings.alphaTexVoiceMode + ); public static readonly noteAccidentalMode = new Map([ ['default', 0], ['forcenone', 1], diff --git a/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageDefinitions.ts b/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageDefinitions.ts index e72824cf1..273e26bed 100644 --- a/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageDefinitions.ts +++ b/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageDefinitions.ts @@ -3,11 +3,19 @@ import type { ArgumentListParseTypesMode } from '@coderline/alphatab/importer/al /** * @target web */ -type SimpleAlphaTexParameterDefinition = - | [AlphaTexNodeType[], ArgumentListParseTypesMode] - | [AlphaTexNodeType[], ArgumentListParseTypesMode, string[]] - | [AlphaTexNodeType[], ArgumentListParseTypesMode, string[] | null, string[]] - | null; +type SimpleAlphaTexParameterDefinition = [ + AlphaTexNodeType[], + ArgumentListParseTypesMode +] | [ + AlphaTexNodeType[], + ArgumentListParseTypesMode, + string[] +] | [ + AlphaTexNodeType[], + ArgumentListParseTypesMode, + string[] | null, + string[] +] | null; /** * @record * @internal @@ -37,38 +45,41 @@ export class AlphaTex1LanguageDefinitions { return { expectedTypes: new Set(simple[0]), parseMode: simple[1], - allowedValues: - simple.length > 2 && simple[2] && simple[2]!.length > 0 ? new Set(simple[2]!) : undefined, - reservedIdentifiers: - simple.length > 3 && simple[3] && simple[3]!.length > 0 ? new Set(simple[3]!) : undefined + allowedValues: simple.length > 2 && simple[2] && simple[2]!.length > 0 ? new Set(simple[2]!) : undefined, + reservedIdentifiers: simple.length > 3 && simple[3] && simple[3]!.length > 0 ? new Set(simple[3]!) : undefined }; } - private static _simple( - signature: (SimpleAlphaTexParameterDefinition | null)[][] | null - ): AlphaTexSignatureDefinition[] | null { + private static _simple(signature: (SimpleAlphaTexParameterDefinition | null)[][] | null): AlphaTexSignatureDefinition[] | null { if (signature == null) { return null; } - return signature.map( - s => - ({ - isStrict: s.length > 0 && s[0] === null, - parameters: s.map(AlphaTex1LanguageDefinitions._param).filter(p => p !== null) - }) as AlphaTexSignatureDefinition - ); + return signature.map(s => ({ + isStrict: s.length > 0 && s[0] === null, + parameters: s.map(AlphaTex1LanguageDefinitions._param).filter(p => p !== null) + }) as AlphaTexSignatureDefinition); } - private static _metaProps(props: [string, [string, SimpleAlphaTexParameterDefinition[][] | null][] | null][]) { - return new Map( - props.map(p => [ - p[0], - p[1] === null ? null : new Map(p[1]!.map(p => [p[0], AlphaTex1LanguageDefinitions._simple(p[1])])) - ]) - ); + private static _metaProps(props: [ + string, + [ + string, + SimpleAlphaTexParameterDefinition[][] | null + ][] | null + ][]) { + return new Map(props.map(p => [ + p[0], + p[1] === null ? null : new Map(p[1]!.map(p => [p[0], AlphaTex1LanguageDefinitions._simple(p[1])])) + ])); } - private static _props(props: [string, SimpleAlphaTexParameterDefinition[][] | null][]) { + private static _props(props: [ + string, + SimpleAlphaTexParameterDefinition[][] | null + ][]) { return new Map(props.map(p => [p[0], AlphaTex1LanguageDefinitions._simple(p[1])])); } - private static _signatures(signatures: [string, SimpleAlphaTexParameterDefinition[][] | null][]) { + private static _signatures(signatures: [ + string, + SimpleAlphaTexParameterDefinition[][] | null + ][]) { return new Map(signatures.map(s => [s[0], AlphaTex1LanguageDefinitions._simple(s[1])])); } // The following definitions age auto-generated from the central definitions in @@ -76,513 +87,138 @@ export class AlphaTex1LanguageDefinitions { // to reduce code size, the parameter types are specified as number values and then // translated inside AlphaTex1LanguageDefinitions._signatures during runtime public static readonly scoreMetaDataSignatures = AlphaTex1LanguageDefinitions._signatures([ - [ - 'title', - [ - [ - [[17, 10], 0], - [[17], 1], - [[10, 17], 1, ['left', 'center', 'right']] - ] - ] - ], - [ - 'subtitle', - [ - [ - [[17, 10], 0], - [[17], 1], - [[10, 17], 1, ['left', 'center', 'right']] - ] - ] - ], - [ - 'artist', - [ - [ - [[17, 10], 0], - [[17], 1], - [[10, 17], 1, ['left', 'center', 'right']] - ] - ] - ], - [ - 'album', - [ - [ - [[17, 10], 0], - [[17], 1], - [[10, 17], 1, ['left', 'center', 'right']] - ] - ] - ], - [ - 'words', - [ - [ - [[17, 10], 0], - [[17], 1], - [[10, 17], 1, ['left', 'center', 'right']] - ] - ] - ], - [ - 'music', - [ - [ - [[17, 10], 0], - [[17], 1], - [[10, 17], 1, ['left', 'center', 'right']] - ] - ] - ], - [ - 'wordsandmusic', - [ - [ - [[17], 0], - [[10, 17], 1, ['left', 'center', 'right']] - ] - ] - ], - [ - 'copyright', - [ - [ - [[17, 10], 0], - [[17], 1], - [[10, 17], 1, ['left', 'center', 'right']] - ] - ] - ], - [ - 'copyright2', - [ - [ - [[17], 0], - [[10, 17], 1, ['left', 'center', 'right']] - ] - ] - ], - ['instructions', [[[[17, 10], 0]]]], - ['notices', [[[[17, 10], 0]]]], - [ - 'tab', - [ - [ - [[17, 10], 0], - [[17], 1], - [[10, 17], 1, ['left', 'center', 'right']] - ] - ] - ], - ['systemslayout', [[[[16], 5]]]], - ['defaultsystemslayout', [[[[16], 0]]]], - ['showdynamics', null], - ['hidedynamics', null], - ['usesystemsignseparator', null], - ['multibarrest', null], - ['bracketextendmode', [[[[10, 17], 0, ['nobrackets', 'groupstaves', 'groupsimilarinstruments']]]]], - ['singletracktracknamepolicy', [[[[10, 17], 0, ['hidden', 'firstsystem', 'allsystems']]]]], - ['multitracktracknamepolicy', [[[[10, 17], 0, ['hidden', 'firstsystem', 'allsystems']]]]], - ['firstsystemtracknamemode', [[[[10, 17], 0, ['fullname', 'shortname']]]]], - ['othersystemstracknamemode', [[[[10, 17], 0, ['fullname', 'shortname']]]]], - ['firstsystemtracknameorientation', [[[[10, 17], 0, ['horizontal', 'vertical']]]]], - ['othersystemstracknameorientation', [[[[10, 17], 0, ['horizontal', 'vertical']]]]], - ['extendbarlines', null] + ["title", [[[[17, 10], 0], [[17], 1], [[10, 17], 1, ["left", "center", "right"]]]]], + ["subtitle", [[[[17, 10], 0], [[17], 1], [[10, 17], 1, ["left", "center", "right"]]]]], + ["artist", [[[[17, 10], 0], [[17], 1], [[10, 17], 1, ["left", "center", "right"]]]]], + ["album", [[[[17, 10], 0], [[17], 1], [[10, 17], 1, ["left", "center", "right"]]]]], + ["words", [[[[17, 10], 0], [[17], 1], [[10, 17], 1, ["left", "center", "right"]]]]], + ["music", [[[[17, 10], 0], [[17], 1], [[10, 17], 1, ["left", "center", "right"]]]]], + ["wordsandmusic", [[[[17], 0], [[10, 17], 1, ["left", "center", "right"]]]]], + ["copyright", [[[[17, 10], 0], [[17], 1], [[10, 17], 1, ["left", "center", "right"]]]]], + ["copyright2", [[[[17], 0], [[10, 17], 1, ["left", "center", "right"]]]]], + ["instructions", [[[[17, 10], 0]]]], + ["notices", [[[[17, 10], 0]]]], + ["tab", [[[[17, 10], 0], [[17], 1], [[10, 17], 1, ["left", "center", "right"]]]]], + ["systemslayout", [[[[16], 5]]]], + ["defaultsystemslayout", [[[[16], 0]]]], + ["showdynamics", null], + ["hidedynamics", null], + ["usesystemsignseparator", null], + ["multibarrest", null], + ["bracketextendmode", [[[[10, 17], 0, ["nobrackets", "groupstaves", "groupsimilarinstruments"]]]]], + ["singletracktracknamepolicy", [[[[10, 17], 0, ["hidden", "firstsystem", "allsystems"]]]]], + ["multitracktracknamepolicy", [[[[10, 17], 0, ["hidden", "firstsystem", "allsystems"]]]]], + ["firstsystemtracknamemode", [[[[10, 17], 0, ["fullname", "shortname"]]]]], + ["othersystemstracknamemode", [[[[10, 17], 0, ["fullname", "shortname"]]]]], + ["firstsystemtracknameorientation", [[[[10, 17], 0, ["horizontal", "vertical"]]]]], + ["othersystemstracknameorientation", [[[[10, 17], 0, ["horizontal", "vertical"]]]]], + ["extendbarlines", null] ]); public static readonly staffMetaDataSignatures = AlphaTex1LanguageDefinitions._signatures([ - ['tuning', [[[[10, 17], 0, ['piano', 'none', 'voice']]], [[[10, 17], 5]]]], - [ - 'chord', - [ - [ - [[17, 10], 0], - [[10, 17, 16], 5] - ] - ] - ], - ['capo', [[[[16], 0]]]], - [ - 'lyrics', - [ - [[[17], 0]], - [ - [[16], 0], - [[17], 0] - ] - ] - ], - [ - 'articulation', - [ - [[[10], 0, ['defaults']]], - [ - [[17, 10], 0], - [[16], 0] - ] - ] - ], - ['displaytranspose', [[[[16], 0]]]], - ['transpose', [[[[16], 0]]]], - ['instrument', [[[[16], 0]], [[[17, 10], 0]], [[[10], 0, ['percussion']]]]] + ["tuning", [[[[10, 17], 0, ["piano", "none", "voice"]]], [[[10, 17], 5]]]], + ["chord", [[[[17, 10], 0], [[10, 17, 16], 5]]]], + ["capo", [[[[16], 0]]]], + ["lyrics", [[[[17], 0]], [[[16], 0], [[17], 0]]]], + ["articulation", [[[[10], 0, ["defaults"]]], [[[17, 10], 0], [[16], 0]]]], + ["displaytranspose", [[[[16], 0]]]], + ["transpose", [[[[16], 0]]]], + ["instrument", [[[[16], 0]], [[[17, 10], 0]], [[[10], 0, ["percussion"]]]]] ]); public static readonly structuralMetaDataSignatures = AlphaTex1LanguageDefinitions._signatures([ - [ - 'track', - [ - [ - [[17], 1], - [[17], 1] - ] - ] - ], - ['staff', null], - ['voice', null] + ["track", [[[[17], 1], [[17], 1]]]], + ["staff", null], + ["voice", null] ]); public static readonly barMetaDataSignatures = AlphaTex1LanguageDefinitions._signatures([ - [ - 'ts', - [ - [[[10, 17], 0, ['common']]], - [ - [[16], 0], - [[16], 0] - ] - ] - ], - ['ro', null], - ['rc', [[[[16], 0]]]], - ['ae', [[[[16, 13], 4]]]], - [ - 'ks', - [ - [ - [ - [10, 17], - 0, - [ - 'cb', - 'gb', - 'db', - 'ab', - 'eb', - 'bb', - 'f', - 'c', - 'g', - 'd', - 'a', - 'e', - 'b', - 'f#', - 'c#', - 'cbmajor', - 'abminor', - 'gbmajor', - 'ebminor', - 'dbmajor', - 'bbminor', - 'abmajor', - 'fminor', - 'ebmajor', - 'cminor', - 'bbmajor', - 'gminor', - 'fmajor', - 'dminor', - 'cmajor', - 'aminor', - 'gmajor', - 'eminor', - 'dmajor', - 'bminor', - 'amajor', - 'f#minor', - 'emajor', - 'c#minor', - 'bmajor', - 'g#minor', - 'f#major', - 'd#minor', - 'f#', - 'c#major', - 'a#minor', - 'c#' - ] - ] - ] - ] - ], - ['clef', [[[[10, 16, 17], 0, ['neutral', 'c3', 'c4', 'f4', 'g2', 'n', 'alto', 'tenor', 'bass', 'treble']]]]], - ['ottava', [[[[10, 17], 0, ['15ma', '8va', 'regular', '8vb', '15mb', '15ma', '8va', '8vb', '15mb']]]]], - [ - 'tempo', - [ - [ - [[16], 2], - [[17], 1] - ], - [null, [[16], 2], [[17], 0], [[16], 1], [[10], 1, ['hide']]] - ] - ], - [ - 'tf', - [ - [ - [ - [10, 16, 17], - 0, - [ - 'none', - 'triplet16th', - 'triplet8th', - 'dotted16th', - 'dotted8th', - 'scottish16th', - 'scottish8th', - 'none', - 'no', - 'notripletfeel', - 't16', - 'triplet-16th', - 't8', - 'triplet-8th', - 'd16', - 'dotted-16th', - 'd8', - 'dotted-8th', - 's16', - 'scottish-16th', - 's8', - 'scottish-8th' - ] - ] - ] - ] - ], - ['ac', null], - [ - 'section', - [ - [[[17, 10], 0]], - [ - [[17, 10], 0], - [[17, 10], 0, null, ['x', '-', 'r']] - ] - ] - ], - [ - 'jump', - [ - [ - [ - [10, 17], - 0, - [ - 'fine', - 'segno', - 'segnosegno', - 'coda', - 'doublecoda', - 'dacapo', - 'dacapoalcoda', - 'dacapoaldoublecoda', - 'dacapoalfine', - 'dalsegno', - 'dalsegnoalcoda', - 'dalsegnoaldoublecoda', - 'dalsegnoalfine', - 'dalsegnosegno', - 'dalsegnosegnoalcoda', - 'dalsegnosegnoaldoublecoda', - 'dalsegnosegnoalfine', - 'dacoda', - 'dadoublecoda' - ] - ] - ] - ] - ], - ['ft', null], - ['simile', [[[[10, 17], 0, ['none', 'simple', 'firstofdouble', 'secondofdouble']]]]], - [ - 'barlineleft', - [ - [ - [ - [10, 17], - 0, - [ - 'automatic', - 'dashed', - 'dotted', - 'heavy', - 'heavyheavy', - 'heavylight', - 'lightheavy', - 'lightlight', - 'none', - 'regular', - 'short', - 'tick' - ] - ] - ] - ] - ], - [ - 'barlineright', - [ - [ - [ - [10, 17], - 0, - [ - 'automatic', - 'dashed', - 'dotted', - 'heavy', - 'heavyheavy', - 'heavylight', - 'lightheavy', - 'lightlight', - 'none', - 'regular', - 'short', - 'tick' - ] - ] - ] - ] - ], - ['scale', [[[[16], 2]]]], - ['width', [[[[16], 2]]]], - [ - 'sync', - [ - [ - [[16], 0], - [[16], 0], - [[16], 0], - [[16], 3] - ] - ] - ], - ['accidentals', [[[[10, 17], 0, ['auto', 'explicit']]]]], - ['spd', [[[[16], 2]]]], - ['sph', [[[[16], 2]]]], - ['spu', [[[[16], 2]]]], - ['db', null] + ["ts", [[[[10, 17], 0, ["common"]]], [[[16], 0], [[16], 0]]]], + ["ro", null], + ["rc", [[[[16], 0]]]], + ["ae", [[[[16, 13], 4]]]], + ["ks", [[[[10, 17], 0, ["cb", "gb", "db", "ab", "eb", "bb", "f", "c", "g", "d", "a", "e", "b", "f#", "c#", "cbmajor", "abminor", "gbmajor", "ebminor", "dbmajor", "bbminor", "abmajor", "fminor", "ebmajor", "cminor", "bbmajor", "gminor", "fmajor", "dminor", "cmajor", "aminor", "gmajor", "eminor", "dmajor", "bminor", "amajor", "f#minor", "emajor", "c#minor", "bmajor", "g#minor", "f#major", "d#minor", "f#", "c#major", "a#minor", "c#"]]]]], + ["clef", [[[[10, 16, 17], 0, ["neutral", "c3", "c4", "f4", "g2", "n", "alto", "tenor", "bass", "treble"]]]]], + ["ottava", [[[[10, 17], 0, ["15ma", "8va", "regular", "8vb", "15mb", "15ma", "8va", "8vb", "15mb"]]]]], + ["tempo", [[[[16], 2], [[17], 1]], [null, [[16], 2], [[17], 0], [[16], 1], [[10], 1, ["hide"]]]]], + ["tf", [[[[10, 16, 17], 0, ["none", "triplet16th", "triplet8th", "dotted16th", "dotted8th", "scottish16th", "scottish8th", "none", "no", "notripletfeel", "t16", "triplet-16th", "t8", "triplet-8th", "d16", "dotted-16th", "d8", "dotted-8th", "s16", "scottish-16th", "s8", "scottish-8th"]]]]], + ["ac", null], + ["section", [[[[17, 10], 0]], [[[17, 10], 0], [[17, 10], 0, null, ["x", "-", "r"]]]]], + ["jump", [[[[10, 17], 0, ["fine", "segno", "segnosegno", "coda", "doublecoda", "dacapo", "dacapoalcoda", "dacapoaldoublecoda", "dacapoalfine", "dalsegno", "dalsegnoalcoda", "dalsegnoaldoublecoda", "dalsegnoalfine", "dalsegnosegno", "dalsegnosegnoalcoda", "dalsegnosegnoaldoublecoda", "dalsegnosegnoalfine", "dacoda", "dadoublecoda"]]]]], + ["ft", null], + ["simile", [[[[10, 17], 0, ["none", "simple", "firstofdouble", "secondofdouble"]]]]], + ["barlineleft", [[[[10, 17], 0, ["automatic", "dashed", "dotted", "heavy", "heavyheavy", "heavylight", "lightheavy", "lightlight", "none", "regular", "short", "tick"]]]]], + ["barlineright", [[[[10, 17], 0, ["automatic", "dashed", "dotted", "heavy", "heavyheavy", "heavylight", "lightheavy", "lightlight", "none", "regular", "short", "tick"]]]]], + ["scale", [[[[16], 2]]]], + ["width", [[[[16], 2]]]], + ["sync", [[[[16], 0], [[16], 0], [[16], 0], [[16], 3]]]], + ["accidentals", [[[[10, 17], 0, ["auto", "explicit"]]]]], + ["spd", [[[[16], 2]]]], + ["sph", [[[[16], 2]]]], + ["spu", [[[[16], 2]]]], + ["db", null], + ["voicemode", [[[[10, 17], 0, ["staffwise", "barwise"]]]]] ]); public static readonly metaDataProperties = AlphaTex1LanguageDefinitions._metaProps([ - [ - 'track', - [ - ['color', [[[[17], 0]]]], - ['systemslayout', [[[[16], 5]]]], - ['defaultsystemslayout', [[[[16], 0]]]], - ['solo', null], - ['mute', null], - ['volume', [[[[16], 0]]]], - ['balance', [[[[16], 0]]]], - ['instrument', [[[[16], 0]], [[[17, 10], 0]], [[[10], 0, ['percussion']]]]], - ['bank', [[[[16], 0]]]], - ['multibarrest', null] - ] - ], - [ - 'staff', - [ - ['score', [[[[16], 1]]]], - ['tabs', null], - ['slash', null], - ['numbered', null] - ] - ], - ['voice', null], - ['title', null], - ['subtitle', null], - ['artist', null], - ['album', null], - ['words', null], - ['music', null], - ['wordsandmusic', null], - ['copyright', null], - ['copyright2', null], - ['instructions', null], - ['notices', null], - ['tab', null], - ['systemslayout', null], - ['defaultsystemslayout', null], - ['showdynamics', null], - ['hidedynamics', null], - ['usesystemsignseparator', null], - ['multibarrest', null], - ['bracketextendmode', null], - ['singletracktracknamepolicy', null], - ['multitracktracknamepolicy', null], - ['firstsystemtracknamemode', null], - ['othersystemstracknamemode', null], - ['firstsystemtracknameorientation', null], - ['othersystemstracknameorientation', null], - ['extendbarlines', null], - [ - 'tuning', - [ - ['hide', null], - ['label', [[[[17], 0]]]] - ] - ], - [ - 'chord', - [ - ['firstfret', [[[[16], 0]]]], - ['barre', [[[[16], 5]]]], - [ - 'showdiagram', - [[], [[[17], 0, ['true', 'false']]], [[[10], 0, ['true', 'false']]], [[[16], 0, ['1', '0']]]] - ], - [ - 'showfingering', - [[], [[[17], 0, ['true', 'false']]], [[[10], 0, ['true', 'false']]], [[[16], 0, ['1', '0']]]] - ], - [ - 'showname', - [[], [[[17], 0, ['true', 'false']]], [[[10], 0, ['true', 'false']]], [[[16], 0, ['1', '0']]]] - ] - ] - ], - ['capo', null], - ['lyrics', null], - ['articulation', null], - ['displaytranspose', null], - ['transpose', null], - ['instrument', null], - ['ts', null], - ['ro', null], - ['rc', null], - ['ae', null], - ['ks', null], - ['clef', null], - ['ottava', null], - ['tempo', null], - ['tf', null], - ['ac', null], - ['section', null], - ['jump', null], - ['ft', null], - ['simile', null], - ['barlineleft', null], - ['barlineright', null], - ['scale', null], - ['width', null], - ['sync', null], - ['accidentals', null], - ['spd', null], - ['sph', null], - ['spu', null], - ['db', null] + ["track", [["color", [[[[17], 0]]]], ["systemslayout", [[[[16], 5]]]], ["defaultsystemslayout", [[[[16], 0]]]], ["solo", null], ["mute", null], ["volume", [[[[16], 0]]]], ["balance", [[[[16], 0]]]], ["instrument", [[[[16], 0]], [[[17, 10], 0]], [[[10], 0, ["percussion"]]]]], ["bank", [[[[16], 0]]]], ["multibarrest", null]]], + ["staff", [["score", [[[[16], 1]]]], ["tabs", null], ["slash", null], ["numbered", null]]], + ["voice", null], + ["title", null], + ["subtitle", null], + ["artist", null], + ["album", null], + ["words", null], + ["music", null], + ["wordsandmusic", null], + ["copyright", null], + ["copyright2", null], + ["instructions", null], + ["notices", null], + ["tab", null], + ["systemslayout", null], + ["defaultsystemslayout", null], + ["showdynamics", null], + ["hidedynamics", null], + ["usesystemsignseparator", null], + ["multibarrest", null], + ["bracketextendmode", null], + ["singletracktracknamepolicy", null], + ["multitracktracknamepolicy", null], + ["firstsystemtracknamemode", null], + ["othersystemstracknamemode", null], + ["firstsystemtracknameorientation", null], + ["othersystemstracknameorientation", null], + ["extendbarlines", null], + ["tuning", [["hide", null], ["label", [[[[17], 0]]]]]], + ["chord", [["firstfret", [[[[16], 0]]]], ["barre", [[[[16], 5]]]], ["showdiagram", [[], [[[17], 0, ["true", "false"]]], [[[10], 0, ["true", "false"]]], [[[16], 0, ["1", "0"]]]]], ["showfingering", [[], [[[17], 0, ["true", "false"]]], [[[10], 0, ["true", "false"]]], [[[16], 0, ["1", "0"]]]]], ["showname", [[], [[[17], 0, ["true", "false"]]], [[[10], 0, ["true", "false"]]], [[[16], 0, ["1", "0"]]]]]]], + ["capo", null], + ["lyrics", null], + ["articulation", null], + ["displaytranspose", null], + ["transpose", null], + ["instrument", null], + ["ts", null], + ["ro", null], + ["rc", null], + ["ae", null], + ["ks", null], + ["clef", null], + ["ottava", null], + ["tempo", null], + ["tf", null], + ["ac", null], + ["section", null], + ["jump", null], + ["ft", null], + ["simile", null], + ["barlineleft", null], + ["barlineright", null], + ["scale", null], + ["width", null], + ["sync", null], + ["accidentals", null], + ["spd", null], + ["sph", null], + ["spu", null], + ["db", null], + ["voicemode", null] ]); public static readonly metaDataSignatures = [ AlphaTex1LanguageDefinitions.scoreMetaDataSignatures, @@ -591,347 +227,100 @@ export class AlphaTex1LanguageDefinitions { AlphaTex1LanguageDefinitions.barMetaDataSignatures ]; public static readonly durationChangeProperties = AlphaTex1LanguageDefinitions._props([ - [ - 'tu', - [ - [[[16], 0, ['3', '5', '6', '7', '9', '10', '12']]], - [ - [[16], 0], - [[16], 0] - ] - ] - ] + ["tu", [[[[16], 0, ["3", "5", "6", "7", "9", "10", "12"]]], [[[16], 0], [[16], 0]]]] ]); public static readonly beatProperties = AlphaTex1LanguageDefinitions._props([ - ['f', null], - ['fo', null], - ['vs', null], - ['v', null], - ['vw', null], - ['s', null], - ['p', null], - ['tt', null], - ['d', null], - ['dd', null], - ['su', null], - ['sd', null], - ['cre', null], - ['dec', null], - ['spd', null], - ['sph', null], - ['spu', null], - ['spe', null], - ['slashed', null], - ['ds', null], - ['glpf', null], - ['glpt', null], - ['waho', null], - ['wahc', null], - ['legatoorigin', null], - ['timer', null], - [ - 'tu', - [ - [[[16], 0, ['3', '5', '6', '7', '9', '10', '12']]], - [ - [[16], 0], - [[16], 0] - ] - ] - ], - ['txt', [[[[17, 10], 0]]]], - [ - 'lyrics', - [ - [[[17], 0]], - [ - [[16], 0], - [[17], 0] - ] - ] - ], - [ - 'tb', - [ - [[[16], 5]], - [ - [[10, 17], 0, ['custom', 'dive', 'dip', 'hold', 'predive', 'predivedive']], - [[16], 5] - ], - [ - [[10, 17], 0, ['default', 'gradual', 'fast']], - [[16], 5] - ], - [ - [[10, 17], 0, ['custom', 'dive', 'dip', 'hold', 'predive', 'predivedive']], - [[10, 17], 0, ['default', 'gradual', 'fast']], - [[16], 5] - ] - ] - ], - [ - 'tbe', - [ - [[[16], 5]], - [ - [[10, 17], 0, ['custom', 'dive', 'dip', 'hold', 'predive', 'predivedive']], - [[16], 5] - ], - [ - [[10, 17], 0, ['default', 'gradual', 'fast']], - [[16], 5] - ], - [ - [[10, 17], 0, ['custom', 'dive', 'dip', 'hold', 'predive', 'predivedive']], - [[10, 17], 0, ['default', 'gradual', 'fast']], - [[16], 5] - ] - ] - ], - ['bu', [[[[16], 1]]]], - ['bd', [[[[16], 1]]]], - ['au', [[[[16], 1]]]], - ['ad', [[[[16], 1]]]], - ['ch', [[[[17, 10], 0]]]], - ['gr', [[[[10, 17], 1, ['onbeat', 'beforebeat', 'bendgrace', 'ob', 'bb', 'b']]]]], - [ - 'dy', - [ - [ - [ - [10, 17], - 0, - [ - 'ppp', - 'pp', - 'p', - 'mp', - 'mf', - 'f', - 'ff', - 'fff', - 'pppp', - 'ppppp', - 'pppppp', - 'ffff', - 'fffff', - 'ffffff', - 'sf', - 'sfp', - 'sfpp', - 'fp', - 'rf', - 'rfz', - 'sfz', - 'sffz', - 'fz', - 'n', - 'pf', - 'sfzp' - ] - ] - ] - ] - ], - [ - 'tempo', - [ - [ - [[16], 0], - [[10], 1, ['hide']] - ], - [ - [[16], 0], - [[17], 0], - [[10], 1, ['hide']] - ] - ] - ], - ['volume', [[[[16], 0]]]], - ['balance', [[[[16], 0]]]], - ['tp', [[[[16], 0, ['8', '16', '32']]]]], - [ - 'barre', - [ - [ - [[16], 0], - [[10, 17], 1, ['full', 'half']] - ] - ] - ], - [ - 'rasg', - [ - [ - [ - [10, 17], - 0, - [ - 'ii', - 'mi', - 'miitriplet', - 'miianapaest', - 'pmptriplet', - 'pmpanapaest', - 'peitriplet', - 'peianapaest', - 'paitriplet', - 'paianapaest', - 'amitriplet', - 'amianapaest', - 'ppp', - 'amii', - 'amip', - 'eami', - 'eamii', - 'peami' - ] - ] - ] - ] - ], - ['ot', [[[[10, 17], 0, ['15ma', '8va', 'regular', '8vb', '15mb', '15ma', '8va', '8vb', '15mb']]]]], - ['instrument', [[[[16], 0]], [[[17, 10], 0]], [[[10], 0, ['percussion']]]]], - ['bank', [[[[16], 0]]]], - [ - 'fermata', - [ - [ - [[10, 17], 0, ['short', 'medium', 'long']], - [[16], 3] - ] - ] - ], - ['beam', [[[[10, 17], 0, ['invert', 'up', 'down', 'auto', 'split', 'merge', 'splitsecondary']]]]] + ["f", null], + ["fo", null], + ["vs", null], + ["v", null], + ["vw", null], + ["s", null], + ["p", null], + ["tt", null], + ["d", null], + ["dd", null], + ["su", null], + ["sd", null], + ["cre", null], + ["dec", null], + ["spd", null], + ["sph", null], + ["spu", null], + ["spe", null], + ["slashed", null], + ["ds", null], + ["glpf", null], + ["glpt", null], + ["waho", null], + ["wahc", null], + ["legatoorigin", null], + ["timer", null], + ["tu", [[[[16], 0, ["3", "5", "6", "7", "9", "10", "12"]]], [[[16], 0], [[16], 0]]]], + ["txt", [[[[17, 10], 0]]]], + ["lyrics", [[[[17], 0]], [[[16], 0], [[17], 0]]]], + ["tb", [[[[16], 5]], [[[10, 17], 0, ["custom", "dive", "dip", "hold", "predive", "predivedive"]], [[16], 5]], [[[10, 17], 0, ["default", "gradual", "fast"]], [[16], 5]], [[[10, 17], 0, ["custom", "dive", "dip", "hold", "predive", "predivedive"]], [[10, 17], 0, ["default", "gradual", "fast"]], [[16], 5]]]], + ["tbe", [[[[16], 5]], [[[10, 17], 0, ["custom", "dive", "dip", "hold", "predive", "predivedive"]], [[16], 5]], [[[10, 17], 0, ["default", "gradual", "fast"]], [[16], 5]], [[[10, 17], 0, ["custom", "dive", "dip", "hold", "predive", "predivedive"]], [[10, 17], 0, ["default", "gradual", "fast"]], [[16], 5]]]], + ["bu", [[[[16], 1]]]], + ["bd", [[[[16], 1]]]], + ["au", [[[[16], 1]]]], + ["ad", [[[[16], 1]]]], + ["ch", [[[[17, 10], 0]]]], + ["gr", [[[[10, 17], 1, ["onbeat", "beforebeat", "bendgrace", "ob", "bb", "b"]]]]], + ["dy", [[[[10, 17], 0, ["ppp", "pp", "p", "mp", "mf", "f", "ff", "fff", "pppp", "ppppp", "pppppp", "ffff", "fffff", "ffffff", "sf", "sfp", "sfpp", "fp", "rf", "rfz", "sfz", "sffz", "fz", "n", "pf", "sfzp"]]]]], + ["tempo", [[[[16], 0], [[10], 1, ["hide"]]], [[[16], 0], [[17], 0], [[10], 1, ["hide"]]]]], + ["volume", [[[[16], 0]]]], + ["balance", [[[[16], 0]]]], + ["tp", [[[[16], 0, ["8", "16", "32"]]]]], + ["barre", [[[[16], 0], [[10, 17], 1, ["full", "half"]]]]], + ["rasg", [[[[10, 17], 0, ["ii", "mi", "miitriplet", "miianapaest", "pmptriplet", "pmpanapaest", "peitriplet", "peianapaest", "paitriplet", "paianapaest", "amitriplet", "amianapaest", "ppp", "amii", "amip", "eami", "eamii", "peami"]]]]], + ["ot", [[[[10, 17], 0, ["15ma", "8va", "regular", "8vb", "15mb", "15ma", "8va", "8vb", "15mb"]]]]], + ["instrument", [[[[16], 0]], [[[17, 10], 0]], [[[10], 0, ["percussion"]]]]], + ["bank", [[[[16], 0]]]], + ["fermata", [[[[10, 17], 0, ["short", "medium", "long"]], [[16], 3]]]], + ["beam", [[[[10, 17], 0, ["invert", "up", "down", "auto", "split", "merge", "splitsecondary"]]]]] ]); public static readonly noteProperties = AlphaTex1LanguageDefinitions._props([ - ['nh', null], - ['ah', [[[[16], 1]]]], - ['th', [[[[16], 1]]]], - ['ph', [[[[16], 1]]]], - ['sh', [[[[16], 1]]]], - ['fh', [[[[16], 1]]]], - ['v', null], - ['vw', null], - ['sl', null], - ['ss', null], - ['sib', null], - ['sia', null], - ['sou', null], - ['sod', null], - ['psu', null], - ['psd', null], - ['h', null], - ['lht', null], - ['g', null], - ['ac', null], - ['hac', null], - ['ten', null], - [ - 'tr', - [ - [ - [[16], 0], - [[16], 1, ['16', '32', '64']] - ] - ] - ], - ['pm', null], - ['st', null], - ['lr', null], - ['x', null], - ['t', null], - ['turn', null], - ['iturn', null], - ['umordent', null], - ['lmordent', null], - ['string', null], - ['hide', null], - [ - 'b', - [ - [[[16], 5]], - [ - [ - [10, 17], - 0, - ['custom', 'bend', 'release', 'bendrelease', 'hold', 'prebend', 'prebendbend', 'prebendrelease'] - ], - [[16], 5] - ], - [ - [[10, 17], 0, ['default', 'gradual', 'fast']], - [[16], 5] - ], - [ - [ - [10, 17], - 0, - ['custom', 'bend', 'release', 'bendrelease', 'hold', 'prebend', 'prebendbend', 'prebendrelease'] - ], - [[10, 17], 0, ['default', 'gradual', 'fast']], - [[16], 5] - ] - ] - ], - [ - 'be', - [ - [[[16], 5]], - [ - [ - [10, 17], - 0, - ['custom', 'bend', 'release', 'bendrelease', 'hold', 'prebend', 'prebendbend', 'prebendrelease'] - ], - [[16], 5] - ], - [ - [[10, 17], 0, ['default', 'gradual', 'fast']], - [[16], 5] - ], - [ - [ - [10, 17], - 0, - ['custom', 'bend', 'release', 'bendrelease', 'hold', 'prebend', 'prebendbend', 'prebendrelease'] - ], - [[10, 17], 0, ['default', 'gradual', 'fast']], - [[16], 5] - ] - ] - ], - ['lf', [[[[16], 0, ['1', '2', '3', '4', '5']]]]], - ['rf', [[[[16], 0, ['1', '2', '3', '4', '5']]]]], - [ - 'acc', - [ - [ - [ - [10, 17], - 0, - [ - 'default', - 'forcenone', - 'forcenatural', - 'forcesharp', - 'forcedoublesharp', - 'forceflat', - 'forcedoubleflat', - 'd', - '-', - 'n', - '#', - '##', - 'x', - 'b', - 'bb' - ] - ] - ] - ] - ], - ['slur', [[[[17], 0]], [[[10], 0]]]], - ['-', null] + ["nh", null], + ["ah", [[[[16], 1]]]], + ["th", [[[[16], 1]]]], + ["ph", [[[[16], 1]]]], + ["sh", [[[[16], 1]]]], + ["fh", [[[[16], 1]]]], + ["v", null], + ["vw", null], + ["sl", null], + ["ss", null], + ["sib", null], + ["sia", null], + ["sou", null], + ["sod", null], + ["psu", null], + ["psd", null], + ["h", null], + ["lht", null], + ["g", null], + ["ac", null], + ["hac", null], + ["ten", null], + ["tr", [[[[16], 0], [[16], 1, ["16", "32", "64"]]]]], + ["pm", null], + ["st", null], + ["lr", null], + ["x", null], + ["t", null], + ["turn", null], + ["iturn", null], + ["umordent", null], + ["lmordent", null], + ["string", null], + ["hide", null], + ["b", [[[[16], 5]], [[[10, 17], 0, ["custom", "bend", "release", "bendrelease", "hold", "prebend", "prebendbend", "prebendrelease"]], [[16], 5]], [[[10, 17], 0, ["default", "gradual", "fast"]], [[16], 5]], [[[10, 17], 0, ["custom", "bend", "release", "bendrelease", "hold", "prebend", "prebendbend", "prebendrelease"]], [[10, 17], 0, ["default", "gradual", "fast"]], [[16], 5]]]], + ["be", [[[[16], 5]], [[[10, 17], 0, ["custom", "bend", "release", "bendrelease", "hold", "prebend", "prebendbend", "prebendrelease"]], [[16], 5]], [[[10, 17], 0, ["default", "gradual", "fast"]], [[16], 5]], [[[10, 17], 0, ["custom", "bend", "release", "bendrelease", "hold", "prebend", "prebendbend", "prebendrelease"]], [[10, 17], 0, ["default", "gradual", "fast"]], [[16], 5]]]], + ["lf", [[[[16], 0, ["1", "2", "3", "4", "5"]]]]], + ["rf", [[[[16], 0, ["1", "2", "3", "4", "5"]]]]], + ["acc", [[[[10, 17], 0, ["default", "forcenone", "forcenatural", "forcesharp", "forcedoublesharp", "forceflat", "forcedoubleflat", "d", "-", "n", "#", "##", "x", "b", "bb"]]]]], + ["slur", [[[[17], 0]], [[[10], 0]]]], + ["-", null] ]); } diff --git a/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageHandler.ts b/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageHandler.ts index 7ace58c4e..62a22f588 100644 --- a/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageHandler.ts +++ b/packages/alphatab/src/importer/alphaTex/AlphaTex1LanguageHandler.ts @@ -761,6 +761,8 @@ export class AlphaTex1LanguageHandler implements IAlphaTexLanguageImportHandler return ApplyNodeResult.Applied; case 'accidentals': return AlphaTex1LanguageHandler._handleAccidentalMode(importer, metaData.arguments!); + case 'voicemode': + return AlphaTex1LanguageHandler._handleVoiceMode(importer, metaData.arguments!); case 'jump': const direction = AlphaTex1LanguageHandler._parseEnumValue( importer, @@ -855,6 +857,20 @@ export class AlphaTex1LanguageHandler implements IAlphaTexLanguageImportHandler return ApplyNodeResult.Applied; } + private static _handleVoiceMode(importer: IAlphaTexImporter, args: AlphaTexArgumentList): ApplyNodeResult { + const voiceMode = AlphaTex1LanguageHandler._parseEnumValue( + importer, + args, + 'voice mode', + AlphaTex1EnumMappings.alphaTexVoiceMode + ); + if (voiceMode === undefined) { + return ApplyNodeResult.NotAppliedSemanticError; + } + importer.state.voiceMode = voiceMode!; + return ApplyNodeResult.Applied; + } + private static _getChordId(currentStaff: Staff, chordName: string): string { return chordName.toLowerCase() + currentStaff.index + currentStaff.track.index; } diff --git a/packages/alphatab/src/importer/alphaTex/AlphaTexShared.ts b/packages/alphatab/src/importer/alphaTex/AlphaTexShared.ts index d48dc1a94..cc8a078c4 100644 --- a/packages/alphatab/src/importer/alphaTex/AlphaTexShared.ts +++ b/packages/alphatab/src/importer/alphaTex/AlphaTexShared.ts @@ -273,12 +273,21 @@ export enum AlphaTexAccidentalMode { Explicit = 1 } +/** + * @public + */ +export enum AlphaTexVoiceMode { + StaffWise = 0, + BarWise = 1 +} + /** * @public */ export interface IAlphaTexImporterState { score: Score; accidentalMode: AlphaTexAccidentalMode; + voiceMode: AlphaTexVoiceMode; currentDynamics: DynamicValue; currentTupletNumerator: number; currentTupletDenominator: number; diff --git a/packages/alphatab/src/importer/alphaTex/_barrel.ts b/packages/alphatab/src/importer/alphaTex/_barrel.ts index 2cf4acbbc..9b522270a 100644 --- a/packages/alphatab/src/importer/alphaTex/_barrel.ts +++ b/packages/alphatab/src/importer/alphaTex/_barrel.ts @@ -43,6 +43,7 @@ export { AlphaTexDiagnosticCode, AlphaTexDiagnosticsSeverity, AlphaTexStaffNoteKind, + AlphaTexVoiceMode, ArgumentListParseTypesMode, type IAlphaTexImporter, type IAlphaTexImporterState diff --git a/packages/alphatab/test/importer/AlphaTexImporterOldNewCompat.test.ts b/packages/alphatab/test/importer/AlphaTexImporterOldNewCompat.test.ts index 71bad14ff..a5359ff89 100644 --- a/packages/alphatab/test/importer/AlphaTexImporterOldNewCompat.test.ts +++ b/packages/alphatab/test/importer/AlphaTexImporterOldNewCompat.test.ts @@ -79,6 +79,7 @@ describe('AlphaTexImporterOldNewCompat', () => { // use exporters to create alphaTex code for comparison test + settings.exporter.comments = true; const exportedOld = new AlphaTexExporterOld().exportToString(expected, settings); await readAndCompare(name, ignoreKeys, exportedOld, settings); diff --git a/packages/alphatab/test/importer/__snapshots__/AlphaTexImporter.test.ts.snap b/packages/alphatab/test/importer/__snapshots__/AlphaTexImporter.test.ts.snap index 63a151f2e..1bde3aee4 100644 --- a/packages/alphatab/test/importer/__snapshots__/AlphaTexImporter.test.ts.snap +++ b/packages/alphatab/test/importer/__snapshots__/AlphaTexImporter.test.ts.snap @@ -313,7 +313,7 @@ Map { "beats" => Array [ Map { "__kind" => "Beat", - "id" => 0, + "id" => 1, "isempty" => true, "automations" => Array [ Map { @@ -330,6 +330,19 @@ Map { }, ], }, + Map { + "__kind" => "Voice", + "id" => 1, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 0, + "isempty" => true, + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, ], }, ], @@ -758,7 +771,7 @@ Map { "beats" => Array [ Map { "__kind" => "Beat", - "id" => 0, + "id" => 1, "isempty" => true, "automations" => Array [ Map { @@ -775,6 +788,19 @@ Map { }, ], }, + Map { + "__kind" => "Voice", + "id" => 1, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 0, + "isempty" => true, + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, ], }, ], @@ -1205,7 +1231,7 @@ Map { "beats" => Array [ Map { "__kind" => "Beat", - "id" => 0, + "id" => 1, "isempty" => true, "automations" => Array [ Map { @@ -1222,6 +1248,19 @@ Map { }, ], }, + Map { + "__kind" => "Voice", + "id" => 1, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 0, + "isempty" => true, + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, ], }, ], @@ -1367,7 +1406,7 @@ Map { "beats" => Array [ Map { "__kind" => "Beat", - "id" => 0, + "id" => 1, "isempty" => true, "automations" => Array [ Map { @@ -1384,6 +1423,19 @@ Map { }, ], }, + Map { + "__kind" => "Voice", + "id" => 1, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 0, + "isempty" => true, + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, ], }, ], @@ -2223,6 +2275,19 @@ Map { Map { "__kind" => "Voice", "id" => 3, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 4, + "isempty" => true, + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + Map { + "__kind" => "Voice", + "id" => 4, "beats" => Array [ Map { "__kind" => "Beat", @@ -2241,11 +2306,24 @@ Map { "voices" => Array [ Map { "__kind" => "Voice", - "id" => 4, + "id" => 5, "beats" => Array [ Map { "__kind" => "Beat", - "id" => 4, + "id" => 5, + "isempty" => true, + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + Map { + "__kind" => "Voice", + "id" => 6, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 6, "isempty" => true, "displayduration" => 960, "playbackduration" => 960, @@ -2260,11 +2338,24 @@ Map { "voices" => Array [ Map { "__kind" => "Voice", - "id" => 5, + "id" => 7, "beats" => Array [ Map { "__kind" => "Beat", - "id" => 5, + "id" => 7, + "isempty" => true, + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + Map { + "__kind" => "Voice", + "id" => 8, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 8, "isempty" => true, "displayduration" => 960, "playbackduration" => 960, @@ -3413,7 +3504,7 @@ Map { "beats" => Array [ Map { "__kind" => "Beat", - "id" => 2, + "id" => 4, "isempty" => true, "automations" => Array [ Map { @@ -3430,6 +3521,19 @@ Map { }, ], }, + Map { + "__kind" => "Voice", + "id" => 4, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 2, + "isempty" => true, + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, ], }, Map { @@ -3438,11 +3542,24 @@ Map { "voices" => Array [ Map { "__kind" => "Voice", - "id" => 4, + "id" => 5, "beats" => Array [ Map { "__kind" => "Beat", - "id" => 4, + "id" => 5, + "isempty" => true, + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + Map { + "__kind" => "Voice", + "id" => 6, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 6, "isempty" => true, "displayduration" => 960, "playbackduration" => 960, @@ -3457,11 +3574,24 @@ Map { "voices" => Array [ Map { "__kind" => "Voice", - "id" => 5, + "id" => 7, "beats" => Array [ Map { "__kind" => "Beat", - "id" => 5, + "id" => 7, + "isempty" => true, + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + Map { + "__kind" => "Voice", + "id" => 8, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 8, "isempty" => true, "displayduration" => 960, "playbackduration" => 960, @@ -4406,7 +4536,7 @@ Map { "beats" => Array [ Map { "__kind" => "Beat", - "id" => 0, + "id" => 1, "isempty" => true, "automations" => Array [ Map { @@ -4423,6 +4553,19 @@ Map { }, ], }, + Map { + "__kind" => "Voice", + "id" => 1, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 0, + "isempty" => true, + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, ], }, ], @@ -6348,3 +6491,435 @@ Map { ], } `; + +exports[`AlphaTexImporterTest voice-mode barWise 1`] = ` +Map { + "__kind" => "Score", + "masterbars" => Array [ + Map { + "__kind" => "MasterBar", + "tempoautomations" => Array [ + Map { + "islinear" => false, + "type" => 0, + "value" => 120, + "ratioposition" => 0, + "text" => "", + "isvisible" => false, + }, + ], + }, + Map { + "__kind" => "MasterBar", + "start" => 3840, + }, + ], + "tracks" => Array [ + Map { + "__kind" => "Track", + "staves" => Array [ + Map { + "__kind" => "Staff", + "bars" => Array [ + Map { + "__kind" => "Bar", + "id" => 0, + "voices" => Array [ + Map { + "__kind" => "Voice", + "id" => 0, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 0, + "notes" => Array [ + Map { + "__kind" => "Note", + "id" => 0, + "octave" => 5, + "tone" => 0, + }, + ], + "automations" => Array [ + Map { + "islinear" => false, + "type" => 2, + "value" => 25, + "ratioposition" => 0, + "text" => "", + "isvisible" => true, + }, + ], + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + Map { + "__kind" => "Voice", + "id" => 1, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 1, + "notes" => Array [ + Map { + "__kind" => "Note", + "id" => 1, + "octave" => 4, + "tone" => 0, + }, + ], + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + ], + }, + Map { + "__kind" => "Bar", + "id" => 1, + "voices" => Array [ + Map { + "__kind" => "Voice", + "id" => 2, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 2, + "notes" => Array [ + Map { + "__kind" => "Note", + "id" => 2, + "octave" => 6, + "tone" => 0, + }, + ], + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + Map { + "__kind" => "Voice", + "id" => 3, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 3, + "notes" => Array [ + Map { + "__kind" => "Note", + "id" => 3, + "octave" => 5, + "tone" => 0, + }, + ], + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + ], + }, + ], + "showtablature" => false, + }, + ], + "playbackinfo" => Map { + "program" => 25, + "secondarychannel" => 1, + }, + }, + ], +} +`; + +exports[`AlphaTexImporterTest voice-mode default 1`] = ` +Map { + "__kind" => "Score", + "masterbars" => Array [ + Map { + "__kind" => "MasterBar", + "tempoautomations" => Array [ + Map { + "islinear" => false, + "type" => 0, + "value" => 120, + "ratioposition" => 0, + "text" => "", + "isvisible" => false, + }, + ], + }, + Map { + "__kind" => "MasterBar", + "start" => 3840, + }, + ], + "tracks" => Array [ + Map { + "__kind" => "Track", + "staves" => Array [ + Map { + "__kind" => "Staff", + "bars" => Array [ + Map { + "__kind" => "Bar", + "id" => 0, + "voices" => Array [ + Map { + "__kind" => "Voice", + "id" => 0, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 0, + "notes" => Array [ + Map { + "__kind" => "Note", + "id" => 0, + "octave" => 5, + "tone" => 0, + }, + ], + "automations" => Array [ + Map { + "islinear" => false, + "type" => 2, + "value" => 25, + "ratioposition" => 0, + "text" => "", + "isvisible" => true, + }, + ], + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + Map { + "__kind" => "Voice", + "id" => 2, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 2, + "notes" => Array [ + Map { + "__kind" => "Note", + "id" => 2, + "octave" => 4, + "tone" => 0, + }, + ], + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + ], + }, + Map { + "__kind" => "Bar", + "id" => 1, + "voices" => Array [ + Map { + "__kind" => "Voice", + "id" => 1, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 1, + "notes" => Array [ + Map { + "__kind" => "Note", + "id" => 1, + "octave" => 6, + "tone" => 0, + }, + ], + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + Map { + "__kind" => "Voice", + "id" => 3, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 3, + "notes" => Array [ + Map { + "__kind" => "Note", + "id" => 3, + "octave" => 5, + "tone" => 0, + }, + ], + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + ], + }, + ], + "showtablature" => false, + }, + ], + "playbackinfo" => Map { + "program" => 25, + "secondarychannel" => 1, + }, + }, + ], +} +`; + +exports[`AlphaTexImporterTest voice-mode staffWise 1`] = ` +Map { + "__kind" => "Score", + "masterbars" => Array [ + Map { + "__kind" => "MasterBar", + "tempoautomations" => Array [ + Map { + "islinear" => false, + "type" => 0, + "value" => 120, + "ratioposition" => 0, + "text" => "", + "isvisible" => false, + }, + ], + }, + Map { + "__kind" => "MasterBar", + "start" => 3840, + }, + ], + "tracks" => Array [ + Map { + "__kind" => "Track", + "staves" => Array [ + Map { + "__kind" => "Staff", + "bars" => Array [ + Map { + "__kind" => "Bar", + "id" => 0, + "voices" => Array [ + Map { + "__kind" => "Voice", + "id" => 0, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 0, + "notes" => Array [ + Map { + "__kind" => "Note", + "id" => 0, + "octave" => 5, + "tone" => 0, + }, + ], + "automations" => Array [ + Map { + "islinear" => false, + "type" => 2, + "value" => 25, + "ratioposition" => 0, + "text" => "", + "isvisible" => true, + }, + ], + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + Map { + "__kind" => "Voice", + "id" => 2, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 2, + "notes" => Array [ + Map { + "__kind" => "Note", + "id" => 2, + "octave" => 4, + "tone" => 0, + }, + ], + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + ], + }, + Map { + "__kind" => "Bar", + "id" => 1, + "voices" => Array [ + Map { + "__kind" => "Voice", + "id" => 1, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 1, + "notes" => Array [ + Map { + "__kind" => "Note", + "id" => 1, + "octave" => 6, + "tone" => 0, + }, + ], + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + Map { + "__kind" => "Voice", + "id" => 3, + "beats" => Array [ + Map { + "__kind" => "Beat", + "id" => 3, + "notes" => Array [ + Map { + "__kind" => "Note", + "id" => 3, + "octave" => 5, + "tone" => 0, + }, + ], + "displayduration" => 960, + "playbackduration" => 960, + }, + ], + }, + ], + }, + ], + "showtablature" => false, + }, + ], + "playbackinfo" => Map { + "program" => 25, + "secondarychannel" => 1, + }, + }, + ], +} +`; diff --git a/packages/alphatex/src/definitions.ts b/packages/alphatex/src/definitions.ts index 5812c3b85..b1056b6ba 100644 --- a/packages/alphatex/src/definitions.ts +++ b/packages/alphatex/src/definitions.ts @@ -147,6 +147,7 @@ import { noteVibratoWide } from '@coderline/alphatab-alphatex//properties/note/v import { x } from '@coderline/alphatab-alphatex//properties/note/x'; import { metadata, properties } from '@coderline/alphatab-alphatex/common'; import { db } from '@coderline/alphatab-alphatex/metadata/bar/db'; +import { voiceMode } from '@coderline/alphatab-alphatex/metadata/bar/voiceMode'; import { extendBarLines } from '@coderline/alphatab-alphatex/metadata/score/extendbarlines'; import { instrumentMeta } from '@coderline/alphatab-alphatex/metadata/staff/instrument'; import type { AlphaTexExample, WithDescription, WithSignatures } from '@coderline/alphatab-alphatex/types'; @@ -216,7 +217,8 @@ export const barMetaData = metadata( spd, sph, spu, - db + db, + voiceMode ); export const allMetadata = new Map([ diff --git a/packages/alphatex/src/enum.ts b/packages/alphatex/src/enum.ts index 1ef2afdd7..162dfd86b 100644 --- a/packages/alphatex/src/enum.ts +++ b/packages/alphatex/src/enum.ts @@ -9,6 +9,7 @@ export const alphaTexMappedEnumLookup = { GraceType: alphaTab.model.GraceType, FermataType: alphaTab.model.FermataType, AlphaTexAccidentalMode: alphaTab.importer.alphaTex.AlphaTexAccidentalMode, + AlphaTexVoiceMode: alphaTab.importer.alphaTex.AlphaTexVoiceMode, NoteAccidentalMode: alphaTab.model.NoteAccidentalMode, BarreShape: alphaTab.model.BarreShape, Ottavia: alphaTab.model.Ottavia, @@ -106,6 +107,18 @@ export const alphaTexMappedEnumMapping: { Auto: { snippet: 'auto', shortDescription: 'Automatic (Based on Pitch)' }, Explicit: { snippet: 'explicit', shortDescription: 'Explicit (as Written)' } }, + AlphaTexVoiceMode: { + StaffWise: { + snippet: 'staffWise', + shortDescription: 'Staff-Wise voices', + longDescription: 'A new voice resets to bar 1 from where the notation of a new voice can be added.' + }, + BarWise: { + snippet: 'barWise', + shortDescription: 'Bar-Wise voices', + longDescription: 'A new voice adds a new voice to the current bar only.' + } + }, NoteAccidentalMode: { Default: { snippet: 'default', shortDescription: 'Auto-detect the accidentals', aliases: ['d'] }, ForceNone: { snippet: 'forceNone', shortDescription: 'Force no accidentals', aliases: ['-'] }, diff --git a/packages/alphatex/src/metadata/bar/voiceMode.ts b/packages/alphatex/src/metadata/bar/voiceMode.ts new file mode 100644 index 000000000..a83eb2598 --- /dev/null +++ b/packages/alphatex/src/metadata/bar/voiceMode.ts @@ -0,0 +1,75 @@ +import * as alphaTab from '@coderline/alphatab'; +import { enumParameter } from '@coderline/alphatab-alphatex/enum'; +import type { MetadataTagDefinition } from '@coderline/alphatab-alphatex/types'; + +export const voiceMode: MetadataTagDefinition = { + tag: '\\voiceMode', + snippet: '\\voiceMode $1 $0', + shortDescription: 'Changes the mode how voices are treated', + longDescription: ` + Changes the mode how alphaTab should treat voices when adding \`\\voice\`. + + You can either choose to write voice-by-voice where each voice has all bars defined. + You write bar-by-bar where a new voice is only added to the current bar. + `, + signatures: [ + { + parameters: [ + { + name: 'mode', + shortDescription: 'The mode which should be active', + parseMode: alphaTab.importer.alphaTex.ArgumentListParseTypesMode.Required, + ...enumParameter('AlphaTexVoiceMode') + } + ] + } + ], + examples: [` + \\title "Staff Wise Voices" + \\voiceMode staffWise + // Voice 1 + \\voice + // Bar 1 Voice 1 + C4*4 | + // Bar 2 Voice 1 + C5*4 | + // Bar 3 Voice 1 + C6*4 + // Voice 2 + \\voice + // Bar 1 Voice 2 + C3*4 | + // Bar 2 Voice 2 + C4*4 | + // Bar 3 Voice 2 + C5 * 4 + `, + ` + \\title "Bar Wise Voices" + \\voiceMode barWise + // Bar 1 + // Bar 1 Voice 1 + \\voice + C4*4 + // Bar 1 Voice 2 + \\voice + C3*4 + | + // Bar 2 + // Bar 2 Voice 1 + \\voice + C5*4 + // Bar 2 Voice 2 + \\voice + C4*4 + | + // Bar 3 + // Bar 3 Voice 1 + \\voice + C6*4 + // Bar 3 Voice 2 + \\voice + C5 * 4 + ` + ] +}; diff --git a/packages/playground/alphatex-editor.ts b/packages/playground/alphatex-editor.ts index 4243a78c1..c03da1dc3 100644 --- a/packages/playground/alphatex-editor.ts +++ b/packages/playground/alphatex-editor.ts @@ -94,7 +94,7 @@ async function setupEditor(api: alphaTab.AlphaTabApi, element: HTMLElement) { }); let fromTex = true; - api.settings.exporter.comments = false; + api.settings.exporter.comments = true; api.settings.exporter.indent = 2; api.scoreLoaded.on(score => { if (!fromTex) {