mirror of
https://github.com/9x/sheetmusictrainer.git
synced 2026-11-03 22:03:54 +01:00
Render dotted durations with visible dots (Dot modifier)
durationCode returned '4d'/'2d' codes but VexFlow only reads the base
duration for glyph/spacing — the dot must be attached explicitly via
Dot.buildAndAttach. Dotted notes rendered without dots, which read like
extra plain quarters ('five quarters in bar 2'). Now dotted notes/rests
show their dot; spacing uses the base code correctly.
This commit is contained in:
@@ -13,7 +13,7 @@
|
|||||||
* microphone frame rate.
|
* microphone frame rate.
|
||||||
*/
|
*/
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { Renderer, Stave, StaveNote, GhostNote, Accidental, Voice, Formatter, StaveConnector, Beam, StaveTie, Annotation } from 'vexflow';
|
import { Renderer, Stave, StaveNote, GhostNote, Accidental, Voice, Formatter, StaveConnector, Beam, StaveTie, Annotation, Dot } from 'vexflow';
|
||||||
import { scoreEvents, type Score } from '../score/model';
|
import { scoreEvents, type Score } from '../score/model';
|
||||||
import { STEP_LETTERS, type StepLetter } from '../score/model';
|
import { STEP_LETTERS, type StepLetter } from '../score/model';
|
||||||
import type { NoteStatus } from '../game/PhraseMatcher';
|
import type { NoteStatus } from '../game/PhraseMatcher';
|
||||||
@@ -165,6 +165,11 @@ export const PhraseSheetMusic: React.FC<PhraseSheetMusicProps> = ({
|
|||||||
const ticks = fragEnd - cursor;
|
const ticks = fragEnd - cursor;
|
||||||
const code = durationCode(ticks);
|
const code = durationCode(ticks);
|
||||||
if (!code) { cursor = fragEnd; continue; }
|
if (!code) { cursor = fragEnd; continue; }
|
||||||
|
// 'd' suffix encodes a dotted duration; the visible dot is
|
||||||
|
// a modifier that must be added explicitly (VexFlow only
|
||||||
|
// reads the base duration for spacing otherwise).
|
||||||
|
const isDotted = code.endsWith('d');
|
||||||
|
const baseCode = isDotted ? code.slice(0, -1) : code;
|
||||||
|
|
||||||
const rest = ev.pitch === null;
|
const rest = ev.pitch === null;
|
||||||
const staveKey: 'treble' | 'bass' = rest
|
const staveKey: 'treble' | 'bass' = rest
|
||||||
@@ -174,7 +179,7 @@ export const PhraseSheetMusic: React.FC<PhraseSheetMusicProps> = ({
|
|||||||
|
|
||||||
let note: StaveNote;
|
let note: StaveNote;
|
||||||
if (rest) {
|
if (rest) {
|
||||||
note = new StaveNote({ keys: ['b/4'], duration: `${code}r`, clef: staveKey });
|
note = new StaveNote({ keys: ['b/4'], duration: `${baseCode}r`, clef: staveKey });
|
||||||
} else {
|
} else {
|
||||||
const p = ev.pitch!;
|
const p = ev.pitch!;
|
||||||
// WRITTEN pitch for the staff: sounding midi + display
|
// WRITTEN pitch for the staff: sounding midi + display
|
||||||
@@ -195,9 +200,10 @@ export const PhraseSheetMusic: React.FC<PhraseSheetMusicProps> = ({
|
|||||||
measureState[stateKey] = written.alter;
|
measureState[stateKey] = written.alter;
|
||||||
note = new StaveNote({
|
note = new StaveNote({
|
||||||
keys: [keyString(written.step, written.alter, written.octave)],
|
keys: [keyString(written.step, written.alter, written.octave)],
|
||||||
duration: code,
|
duration: baseCode,
|
||||||
clef: staveKey,
|
clef: staveKey,
|
||||||
});
|
});
|
||||||
|
if (isDotted) Dot.buildAndAttach([note]);
|
||||||
if (glyph) note.addModifier(new Accidental(glyph));
|
if (glyph) note.addModifier(new Accidental(glyph));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user