@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.
@@ -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
- if (event.grace) {
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 { str: eventStr, newEnv } = serializeEvent(event, pitchEnv, prevDuration);
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