@k-l-lambda/lilylet 0.1.31 → 0.1.32
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/meiEncoder.js +17 -3
- package/package.json +1 -1
- package/source/lilylet/meiEncoder.ts +15 -5
package/lib/meiEncoder.js
CHANGED
|
@@ -128,20 +128,24 @@ const encodePitch = (pitch, keyFifths = 0, ottavaShift = 0) => {
|
|
|
128
128
|
// Get the accidental implied by the key signature for this note
|
|
129
129
|
const keyAccidentals = getKeyAccidentals(keyFifths);
|
|
130
130
|
const keyAccid = keyAccidentals[pitch.phonet];
|
|
131
|
-
// Determine
|
|
131
|
+
// Determine accid (written/displayed) and accid.ges (gestural/sounding)
|
|
132
132
|
let accid;
|
|
133
|
+
let accidGes;
|
|
133
134
|
if (pitch.accidental) {
|
|
134
135
|
const noteAccid = ACCIDENTALS[pitch.accidental];
|
|
135
|
-
// Only output accid if it's different from what the key implies
|
|
136
136
|
if (noteAccid !== keyAccid) {
|
|
137
|
+
// Accidental differs from key signature - display it
|
|
137
138
|
accid = noteAccid;
|
|
138
139
|
}
|
|
140
|
+
// Always set gestural accidental for MIDI generation
|
|
141
|
+
accidGes = noteAccid;
|
|
139
142
|
}
|
|
140
143
|
else if (keyAccid) {
|
|
141
144
|
// Note has no accidental but key implies one - output natural
|
|
142
145
|
accid = 'n';
|
|
146
|
+
accidGes = 'n';
|
|
143
147
|
}
|
|
144
|
-
return { pname: pitch.phonet, oct, accid };
|
|
148
|
+
return { pname: pitch.phonet, oct, accid, accidGes };
|
|
145
149
|
};
|
|
146
150
|
// Convert tremolo division to stem.mod value
|
|
147
151
|
const tremoloToStemMod = (division) => {
|
|
@@ -161,6 +165,8 @@ const buildNoteElement = (pitch, dur, dots, indent, inChord, options = {}, noteI
|
|
|
161
165
|
}
|
|
162
166
|
if (pitch.accid)
|
|
163
167
|
attrs += ` accid="${pitch.accid}"`;
|
|
168
|
+
if (pitch.accidGes)
|
|
169
|
+
attrs += ` accid.ges="${pitch.accidGes}"`;
|
|
164
170
|
if (!inChord && dots > 0)
|
|
165
171
|
attrs += ` dots="${dots}"`;
|
|
166
172
|
if (!inChord && options.grace)
|
|
@@ -566,6 +572,8 @@ const tremoloEventToMEI = (event, indent, keyFifths = 0, ottavaShift = 0) => {
|
|
|
566
572
|
let attrs = `xml:id="${generateId('note')}" pname="${pitch.pname}" oct="${pitch.oct}" dur="${noteDur}"`;
|
|
567
573
|
if (pitch.accid)
|
|
568
574
|
attrs += ` accid="${pitch.accid}"`;
|
|
575
|
+
if (pitch.accidGes)
|
|
576
|
+
attrs += ` accid.ges="${pitch.accidGes}"`;
|
|
569
577
|
result += `${indent} <note ${attrs} />\n`;
|
|
570
578
|
}
|
|
571
579
|
else if (event.pitchA.length > 1) {
|
|
@@ -575,6 +583,8 @@ const tremoloEventToMEI = (event, indent, keyFifths = 0, ottavaShift = 0) => {
|
|
|
575
583
|
let attrs = `xml:id="${generateId('note')}" pname="${pitch.pname}" oct="${pitch.oct}"`;
|
|
576
584
|
if (pitch.accid)
|
|
577
585
|
attrs += ` accid="${pitch.accid}"`;
|
|
586
|
+
if (pitch.accidGes)
|
|
587
|
+
attrs += ` accid.ges="${pitch.accidGes}"`;
|
|
578
588
|
result += `${indent} <note ${attrs} />\n`;
|
|
579
589
|
}
|
|
580
590
|
result += `${indent} </chord>\n`;
|
|
@@ -585,6 +595,8 @@ const tremoloEventToMEI = (event, indent, keyFifths = 0, ottavaShift = 0) => {
|
|
|
585
595
|
let attrs = `xml:id="${generateId('note')}" pname="${pitch.pname}" oct="${pitch.oct}" dur="${noteDur}"`;
|
|
586
596
|
if (pitch.accid)
|
|
587
597
|
attrs += ` accid="${pitch.accid}"`;
|
|
598
|
+
if (pitch.accidGes)
|
|
599
|
+
attrs += ` accid.ges="${pitch.accidGes}"`;
|
|
588
600
|
result += `${indent} <note ${attrs} />\n`;
|
|
589
601
|
}
|
|
590
602
|
else if (event.pitchB.length > 1) {
|
|
@@ -594,6 +606,8 @@ const tremoloEventToMEI = (event, indent, keyFifths = 0, ottavaShift = 0) => {
|
|
|
594
606
|
let attrs = `xml:id="${generateId('note')}" pname="${pitch.pname}" oct="${pitch.oct}"`;
|
|
595
607
|
if (pitch.accid)
|
|
596
608
|
attrs += ` accid="${pitch.accid}"`;
|
|
609
|
+
if (pitch.accidGes)
|
|
610
|
+
attrs += ` accid.ges="${pitch.accidGes}"`;
|
|
597
611
|
result += `${indent} <note ${attrs} />\n`;
|
|
598
612
|
}
|
|
599
613
|
result += `${indent} </chord>\n`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@k-l-lambda/lilylet",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"description": "Lilylet is a lilyopnd-like sheet music language designed for Markdown rendering and symbolic music representation in AIGC applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -171,7 +171,7 @@ const getKeyAccidentals = (fifths: number): Record<string, string> => {
|
|
|
171
171
|
// Convert Pitch to MEI attributes, checking against key signature
|
|
172
172
|
// ottavaShift: current ottava level (1 = 8va up, -1 = 8vb down, 2 = 15ma up, etc.)
|
|
173
173
|
// The written pitch should be adjusted by subtracting the ottava shift
|
|
174
|
-
const encodePitch = (pitch: Pitch, keyFifths: number = 0, ottavaShift: number = 0): { pname: string; oct: number; accid?: string } => {
|
|
174
|
+
const encodePitch = (pitch: Pitch, keyFifths: number = 0, ottavaShift: number = 0): { pname: string; oct: number; accid?: string; accidGes?: string } => {
|
|
175
175
|
// Lilylet octave: 0 = middle C octave (C4), positive = higher, negative = lower
|
|
176
176
|
// When ottava is active, the source pitch is the sounding pitch, but we need to output the written pitch
|
|
177
177
|
// For 8va up (ottavaShift=1), written pitch is one octave lower than sounding
|
|
@@ -181,20 +181,25 @@ const encodePitch = (pitch: Pitch, keyFifths: number = 0, ottavaShift: number =
|
|
|
181
181
|
const keyAccidentals = getKeyAccidentals(keyFifths);
|
|
182
182
|
const keyAccid = keyAccidentals[pitch.phonet];
|
|
183
183
|
|
|
184
|
-
// Determine
|
|
184
|
+
// Determine accid (written/displayed) and accid.ges (gestural/sounding)
|
|
185
185
|
let accid: string | undefined;
|
|
186
|
+
let accidGes: string | undefined;
|
|
187
|
+
|
|
186
188
|
if (pitch.accidental) {
|
|
187
189
|
const noteAccid = ACCIDENTALS[pitch.accidental];
|
|
188
|
-
// Only output accid if it's different from what the key implies
|
|
189
190
|
if (noteAccid !== keyAccid) {
|
|
191
|
+
// Accidental differs from key signature - display it
|
|
190
192
|
accid = noteAccid;
|
|
191
193
|
}
|
|
194
|
+
// Always set gestural accidental for MIDI generation
|
|
195
|
+
accidGes = noteAccid;
|
|
192
196
|
} else if (keyAccid) {
|
|
193
197
|
// Note has no accidental but key implies one - output natural
|
|
194
198
|
accid = 'n';
|
|
199
|
+
accidGes = 'n';
|
|
195
200
|
}
|
|
196
201
|
|
|
197
|
-
return { pname: pitch.phonet, oct, accid };
|
|
202
|
+
return { pname: pitch.phonet, oct, accid, accidGes };
|
|
198
203
|
};
|
|
199
204
|
|
|
200
205
|
|
|
@@ -210,7 +215,7 @@ const tremoloToStemMod = (division: number): string | undefined => {
|
|
|
210
215
|
|
|
211
216
|
// Build note element
|
|
212
217
|
const buildNoteElement = (
|
|
213
|
-
pitch: { pname: string; oct: number; accid?: string },
|
|
218
|
+
pitch: { pname: string; oct: number; accid?: string; accidGes?: string },
|
|
214
219
|
dur: string,
|
|
215
220
|
dots: number,
|
|
216
221
|
indent: string,
|
|
@@ -233,6 +238,7 @@ const buildNoteElement = (
|
|
|
233
238
|
attrs += ` dur="${dur}"`;
|
|
234
239
|
}
|
|
235
240
|
if (pitch.accid) attrs += ` accid="${pitch.accid}"`;
|
|
241
|
+
if (pitch.accidGes) attrs += ` accid.ges="${pitch.accidGes}"`;
|
|
236
242
|
if (!inChord && dots > 0) attrs += ` dots="${dots}"`;
|
|
237
243
|
if (!inChord && options.grace) attrs += ` grace="unacc"`;
|
|
238
244
|
if (!inChord && options.tie) attrs += ` tie="${options.tie}"`;
|
|
@@ -715,6 +721,7 @@ const tremoloEventToMEI = (event: TremoloEvent, indent: string, keyFifths: numbe
|
|
|
715
721
|
const pitch = encodePitch(event.pitchA[0], keyFifths, ottavaShift);
|
|
716
722
|
let attrs = `xml:id="${generateId('note')}" pname="${pitch.pname}" oct="${pitch.oct}" dur="${noteDur}"`;
|
|
717
723
|
if (pitch.accid) attrs += ` accid="${pitch.accid}"`;
|
|
724
|
+
if (pitch.accidGes) attrs += ` accid.ges="${pitch.accidGes}"`;
|
|
718
725
|
result += `${indent} <note ${attrs} />\n`;
|
|
719
726
|
} else if (event.pitchA.length > 1) {
|
|
720
727
|
result += `${indent} <chord xml:id="${generateId('chord')}" dur="${noteDur}">\n`;
|
|
@@ -722,6 +729,7 @@ const tremoloEventToMEI = (event: TremoloEvent, indent: string, keyFifths: numbe
|
|
|
722
729
|
const pitch = encodePitch(p, keyFifths, ottavaShift);
|
|
723
730
|
let attrs = `xml:id="${generateId('note')}" pname="${pitch.pname}" oct="${pitch.oct}"`;
|
|
724
731
|
if (pitch.accid) attrs += ` accid="${pitch.accid}"`;
|
|
732
|
+
if (pitch.accidGes) attrs += ` accid.ges="${pitch.accidGes}"`;
|
|
725
733
|
result += `${indent} <note ${attrs} />\n`;
|
|
726
734
|
}
|
|
727
735
|
result += `${indent} </chord>\n`;
|
|
@@ -732,6 +740,7 @@ const tremoloEventToMEI = (event: TremoloEvent, indent: string, keyFifths: numbe
|
|
|
732
740
|
const pitch = encodePitch(event.pitchB[0], keyFifths, ottavaShift);
|
|
733
741
|
let attrs = `xml:id="${generateId('note')}" pname="${pitch.pname}" oct="${pitch.oct}" dur="${noteDur}"`;
|
|
734
742
|
if (pitch.accid) attrs += ` accid="${pitch.accid}"`;
|
|
743
|
+
if (pitch.accidGes) attrs += ` accid.ges="${pitch.accidGes}"`;
|
|
735
744
|
result += `${indent} <note ${attrs} />\n`;
|
|
736
745
|
} else if (event.pitchB.length > 1) {
|
|
737
746
|
result += `${indent} <chord xml:id="${generateId('chord')}" dur="${noteDur}">\n`;
|
|
@@ -739,6 +748,7 @@ const tremoloEventToMEI = (event: TremoloEvent, indent: string, keyFifths: numbe
|
|
|
739
748
|
const pitch = encodePitch(p, keyFifths, ottavaShift);
|
|
740
749
|
let attrs = `xml:id="${generateId('note')}" pname="${pitch.pname}" oct="${pitch.oct}"`;
|
|
741
750
|
if (pitch.accid) attrs += ` accid="${pitch.accid}"`;
|
|
751
|
+
if (pitch.accidGes) attrs += ` accid.ges="${pitch.accidGes}"`;
|
|
742
752
|
result += `${indent} <note ${attrs} />\n`;
|
|
743
753
|
}
|
|
744
754
|
result += `${indent} </chord>\n`;
|