Draw fingering charts foot-up, in grouped rows.
AppImage release / appimage (push) Successful in 1m36s
AppImage release / appimage (push) Successful in 1m36s
The foot and the double holes sit at the top of each narrow chart, and the mouthpiece sits at the bottom, matching hand-drawn tabs. Which holes are open or closed is unchanged. A repeat mark is shown only when the score repeats. Version 0.1.2. Co-authored-by: catpersonisme <catpersonisme@gmail.com>
This commit is contained in:
9 files changed
+314
-122
No files matched your search
+46
-1
@@ -4,7 +4,7 @@ import { describe, expect, it } from "vitest";
|
||||
import { fingeringCode, fingeringFor, fingeringMarks, CHART_NOTES } from "@/lib/fingering";
|
||||
import { parseMusicXml } from "@/lib/musicxml";
|
||||
import { parsePitch } from "@/lib/pitch";
|
||||
import { barStatus, expectedQuarters } from "@/lib/score";
|
||||
import { barStatus, expectedQuarters, extraBeatArrows, leadRows } from "@/lib/score";
|
||||
import { odeToJoy, twinkle } from "@/lib/samples";
|
||||
import { formatTextTab } from "@/lib/text-tab";
|
||||
|
||||
@@ -121,5 +121,50 @@ describe("lead-line MusicXML", () => {
|
||||
expect(parsed.warnings.join(" ")).toMatch(/lower staves/);
|
||||
expect(parsed.warnings.join(" ")).toMatch(/highest note/);
|
||||
expect(parsed.warnings.join(" ")).toMatch(/Grace notes/);
|
||||
expect(parsed.score.measures.some((measure) => measure.repeatTimes || measure.repeatForward)).toBe(false);
|
||||
});
|
||||
|
||||
it("shows a repeat count only when the barline repeats", () => {
|
||||
const xml = `<?xml version="1.0"?>
|
||||
<score-partwise version="4.0">
|
||||
<part-list><score-part id="P1"><part-name>Lead</part-name></score-part></part-list>
|
||||
<part id="P1">
|
||||
<measure number="1">
|
||||
<barline location="left"><repeat direction="forward"/></barline>
|
||||
<attributes><divisions>1</divisions><time><beats>4</beats><beat-type>4</beat-type></time></attributes>
|
||||
<note><pitch><step>C</step><octave>5</octave></pitch><duration>1</duration><type>quarter</type></note>
|
||||
<note><rest/><duration>3</duration><type>half</type><dot/></note>
|
||||
</measure>
|
||||
<measure number="2">
|
||||
<note><pitch><step>D</step><octave>5</octave></pitch><duration>4</duration><type>whole</type></note>
|
||||
<barline location="right"><repeat direction="backward" times="2"/></barline>
|
||||
</measure>
|
||||
</part>
|
||||
</score-partwise>`;
|
||||
const parsed = parseMusicXml(xml);
|
||||
expect(parsed.ok).toBe(true);
|
||||
if (!parsed.ok) return;
|
||||
expect(parsed.score.measures[0].repeatForward).toBe(true);
|
||||
expect(parsed.score.measures[0].repeatTimes).toBeUndefined();
|
||||
expect(parsed.score.measures[1].repeatTimes).toBe(2);
|
||||
expect(parsed.score.measures[1].events.map((event) => event.label)).toEqual(["whole"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("lead layout", () => {
|
||||
it("counts extra beats without adding an arrow for a dotted quarter", () => {
|
||||
const fourFour = { beats: 4, beatType: 4 };
|
||||
expect(extraBeatArrows(1, fourFour)).toBe(0);
|
||||
expect(extraBeatArrows(0.5, fourFour)).toBe(0);
|
||||
expect(extraBeatArrows(1.5, fourFour)).toBe(0);
|
||||
expect(extraBeatArrows(2, fourFour)).toBe(1);
|
||||
expect(extraBeatArrows(3, fourFour)).toBe(2);
|
||||
expect(extraBeatArrows(4, fourFour)).toBe(3);
|
||||
});
|
||||
|
||||
it("keeps Twinkle in three rows and does not invent a repeat", () => {
|
||||
expect(leadRows(twinkle.measures)).toHaveLength(3);
|
||||
expect(twinkle.measures.some((measure) => measure.repeatTimes)).toBe(false);
|
||||
expect(odeToJoy.measures.some((measure) => measure.repeatTimes)).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user