@k-l-lambda/lilylet 0.1.68 → 0.1.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/abc/grammar.jison.js +155 -135
- package/lib/lilylet/abcDecoder.js +3 -3
- package/lib/lilylet/serializer.js +23 -6
- package/package.json +1 -1
- package/source/abc/abc.jison +37 -12
- package/source/abc/grammar.jison.js +155 -135
- package/source/lilylet/abcDecoder.ts +4 -3
- package/source/lilylet/serializer.ts +27 -6
|
@@ -768,7 +768,7 @@ const processBarPatch = (
|
|
|
768
768
|
|
|
769
769
|
// Grace notes
|
|
770
770
|
if ((term as any).grace) {
|
|
771
|
-
const graceEvents = convertGraceEvents(term as any, unitLength);
|
|
771
|
+
const graceEvents = convertGraceEvents(term as any, unitLength, pitchCtx);
|
|
772
772
|
events.push(...graceEvents);
|
|
773
773
|
i++;
|
|
774
774
|
continue;
|
|
@@ -955,7 +955,8 @@ const convertEventTerm = (
|
|
|
955
955
|
*/
|
|
956
956
|
const convertGraceEvents = (
|
|
957
957
|
graceTerm: any,
|
|
958
|
-
unitLength: { numerator: number; denominator: number }
|
|
958
|
+
unitLength: { numerator: number; denominator: number },
|
|
959
|
+
pitchCtx?: PitchContext
|
|
959
960
|
): NoteEvent[] => {
|
|
960
961
|
const events: NoteEvent[] = [];
|
|
961
962
|
if (!graceTerm.events) return events;
|
|
@@ -968,7 +969,7 @@ const convertGraceEvents = (
|
|
|
968
969
|
|
|
969
970
|
const pitches = chord.pitches.filter((p: ABC.Pitch) =>
|
|
970
971
|
p.phonet !== "z" && p.phonet !== "Z" && p.phonet !== "x"
|
|
971
|
-
).map(convertPitch);
|
|
972
|
+
).map((p: ABC.Pitch) => convertPitch(p, pitchCtx));
|
|
972
973
|
|
|
973
974
|
if (pitches.length === 0) continue;
|
|
974
975
|
|
|
@@ -278,13 +278,15 @@ const serializeMarks = (marks: Mark[]): string => {
|
|
|
278
278
|
const serializeNoteEvent = (
|
|
279
279
|
event: NoteEvent,
|
|
280
280
|
env: PitchEnv,
|
|
281
|
-
prevDuration?: Duration
|
|
281
|
+
prevDuration?: Duration,
|
|
282
|
+
suppressGracePrefix: boolean = false
|
|
282
283
|
): { str: string; newEnv: PitchEnv } => {
|
|
283
284
|
const parts: string[] = [];
|
|
284
285
|
let currentEnv = env;
|
|
285
286
|
|
|
286
|
-
// Grace note prefix
|
|
287
|
-
|
|
287
|
+
// Grace note prefix. When the caller groups consecutive grace notes into a single
|
|
288
|
+
// scoped \grace { ... }, it suppresses the per-note prefix and emits the wrapper.
|
|
289
|
+
if (event.grace && !suppressGracePrefix) {
|
|
288
290
|
parts.push('\\grace ');
|
|
289
291
|
}
|
|
290
292
|
|
|
@@ -564,11 +566,12 @@ const serializeBarlineEvent = (event: BarlineEvent): string => {
|
|
|
564
566
|
const serializeEvent = (
|
|
565
567
|
event: Event,
|
|
566
568
|
env: PitchEnv,
|
|
567
|
-
prevDuration?: Duration
|
|
569
|
+
prevDuration?: Duration,
|
|
570
|
+
suppressGracePrefix: boolean = false
|
|
568
571
|
): { str: string; newEnv: PitchEnv } => {
|
|
569
572
|
switch (event.type) {
|
|
570
573
|
case 'note':
|
|
571
|
-
return serializeNoteEvent(event as NoteEvent, env, prevDuration);
|
|
574
|
+
return serializeNoteEvent(event as NoteEvent, env, prevDuration, suppressGracePrefix);
|
|
572
575
|
case 'rest':
|
|
573
576
|
return serializeRestEvent(event as RestEvent, env, prevDuration);
|
|
574
577
|
case 'context':
|
|
@@ -716,6 +719,7 @@ const serializeVoice = (
|
|
|
716
719
|
|
|
717
720
|
let activeStaff = effectiveInitialStaff;
|
|
718
721
|
let activeStemDir: StemDirection | undefined;
|
|
722
|
+
let graceGroupOpen = false; // whether a scoped \grace { ... } is currently open
|
|
719
723
|
|
|
720
724
|
for (let eventIdx = 0; eventIdx < voice.events.length; eventIdx++) {
|
|
721
725
|
const event = voice.events[eventIdx];
|
|
@@ -781,7 +785,19 @@ const serializeVoice = (
|
|
|
781
785
|
}
|
|
782
786
|
}
|
|
783
787
|
|
|
784
|
-
const
|
|
788
|
+
const isGraceNote = event.type === 'note' && !!(event as NoteEvent).grace;
|
|
789
|
+
|
|
790
|
+
// Group consecutive grace notes into one scoped \grace { ... } instead of
|
|
791
|
+
// emitting a separate \grace prefix per note.
|
|
792
|
+
if (isGraceNote && !graceGroupOpen) {
|
|
793
|
+
parts.push('\\grace {');
|
|
794
|
+
graceGroupOpen = true;
|
|
795
|
+
} else if (!isGraceNote && graceGroupOpen) {
|
|
796
|
+
parts.push('}');
|
|
797
|
+
graceGroupOpen = false;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
const { str: eventStr, newEnv } = serializeEvent(event, pitchEnv, prevDuration, graceGroupOpen);
|
|
785
801
|
pitchEnv = newEnv;
|
|
786
802
|
|
|
787
803
|
if (eventStr) {
|
|
@@ -805,6 +821,11 @@ const serializeVoice = (
|
|
|
805
821
|
}
|
|
806
822
|
}
|
|
807
823
|
|
|
824
|
+
// Close a grace group left open at the end of the voice (unusual but possible).
|
|
825
|
+
if (graceGroupOpen) {
|
|
826
|
+
parts.push('}');
|
|
827
|
+
}
|
|
828
|
+
|
|
808
829
|
return { str: parts.join(' '), newStaff: voice.staff };
|
|
809
830
|
};
|
|
810
831
|
|