@k-l-lambda/lilylet 0.1.35 → 0.1.37

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/README.md CHANGED
@@ -1,7 +1,37 @@
1
1
  # Lilylet
2
2
 
3
+ ![](./docs/live-demo.gif)
4
+
3
5
  Lilylet is a LilyPond-like music notation language designed for Markdown rendering and symbolic music representation in AIGC applications.
4
6
 
7
+ ## Hello World
8
+
9
+ ```lyl
10
+ \key c \major \time 4/4 \clef "treble" c1 \bar "|."
11
+ ```
12
+ ![](./docs/hello-world.svg)
13
+
14
+
15
+ ## A Comprehensive Example
16
+
17
+ ```lyl
18
+ [title "Jesu, meine Freude"]
19
+ [subtitle "BWV 610"]
20
+ [composer "J.S. Bach"]
21
+
22
+ \staff "1" \key c \minor \time 4/4 \clef "treble" \stemUp g'4 g f ef \\
23
+ \staff "1" \stemDown ef16[ d ef8]~ ef16[ f ef d] c8[ d]~ d[ c] \\
24
+ \staff "2" \clef "bass" c16[ b c8]~ c16[ b c g] a8[ g]~ g16[ g af ef] \\
25
+ \staff "3" \clef "bass" r8 c,16[ d] ef[ d ef8]~ ef16[ a, b g] c[ b c8] | % 1
26
+
27
+ \staff "1" \stemUp d2 c\fermata \\
28
+ \staff "1" \stemDown c8[ c4 b8] c8.[ \staff "2" \stemUp g16] \staff "1" c[ b c d] \\
29
+ \staff "2" f,16[ ef f d] g[ af g f] ef[ d ef8]~ ef16[ f ef d] \\
30
+ \staff "3" r16 g,[ af f] g[ f g8] c,2 | % 2
31
+ ```
32
+ ![](./docs/BWV610.svg)
33
+
34
+
5
35
  ## Try It Online
6
36
 
7
37
  - [Live Editor](https://k-l-lambda.github.io/lilylet-live-editor/) - Interactive editor with real-time music notation rendering
@@ -33,28 +63,3 @@ LilyPond is powerful but overly flexible—the same music can be written in mult
33
63
  - **Shorter context description**: Removes redundant information, allowing LLMs to process more music content within limited context windows
34
64
  - **Formatted layout**: Fixed syntax structure facilitates model learning and generation
35
65
  - **Markdown-embeddable**: Music snippets can be directly embedded in documents
36
-
37
- ### Basic Syntax
38
-
39
- | Element | Syntax | Description |
40
- |---------|--------|-------------|
41
- | Staff | `\staff "1"` | Specifies which staff the current voice belongs to |
42
- | Key | `\key c \major` | C major |
43
- | Time | `\time 4/4` | 4/4 time signature |
44
- | Clef | `\clef "treble"` | Treble clef |
45
- | Notes | `c4 d8 e16` | C quarter note, D eighth note, E sixteenth note |
46
- | Accidentals | `cs` `cf` `css` `cff` | C sharp, C flat, C double-sharp, C double-flat |
47
- | Octave | `c'` `c,` | One octave higher, one octave lower |
48
- | Chord | `<c e g>4` | C major triad, quarter note |
49
- | Voice separator | `\\` | Separates multiple voices within the same staff |
50
- | Part separator | `\\\` | Separates different instrument tracks (parts) in a score |
51
- | Bar line | `\|` | Separates measures |
52
-
53
- ## Syntax Example
54
-
55
- ```lilylet
56
- \staff "1" \key e \major \time 2/4 \clef "treble" \stemUp e8 [ ds16 e16 ] fs4 ~ \\
57
- \staff "1" s4 \stemDown ds4 ~ \\
58
- \staff "2" \key e \major \clef "bass" \stemUp e,,4 b4 \\
59
- \staff "2" \stemDown e,,16 [ b'8 -> b16 ] b,16 [ b'8 -> b16 ] |
60
- ```
@@ -150,7 +150,7 @@ case 25:
150
150
  this.$ = $$[$0-3].concat([part($$[$0])]);
151
151
  break;
152
152
  case 26:
153
- currentStaff = 1;
153
+ currentStaff = 1; currentOttava = 0;
154
154
  break;
155
155
  case 27:
156
156
  this.$ = [voice(currentStaff, $$[$0])];
@@ -174,7 +174,16 @@ case 43:
174
174
  this.$ = markupEvent($$[$0].slice(1, -1));
175
175
  break;
176
176
  case 44:
177
- this.$ = ({ type: 'pitchReset' });
177
+
178
+ // On newline, reset ottava to 0 if it's non-zero (like pitch base resets)
179
+ if (currentOttava !== 0) {
180
+ const ottavaReset = contextChange({ ottava: 0 });
181
+ currentOttava = 0;
182
+ this.$ = [ottavaReset, { type: 'pitchReset' }];
183
+ } else {
184
+ this.$ = { type: 'pitchReset' };
185
+ }
186
+
178
187
  break;
179
188
  case 45: case 46:
180
189
  currentDuration = $$[$0-1]; this.$ = noteEvent($$[$0-2], $$[$0-1], $$[$0]);
@@ -299,13 +308,13 @@ case 85:
299
308
  currentStaff = Number($$[$0].slice(1, -1)); this.$ = currentStaff;
300
309
  break;
301
310
  case 86:
302
- this.$ = Number($$[$0]);
311
+ currentOttava = Number($$[$0]); this.$ = currentOttava;
303
312
  break;
304
313
  case 87:
305
- this.$ = -Number($$[$0]);
314
+ currentOttava = -Number($$[$0]); this.$ = currentOttava;
306
315
  break;
307
316
  case 88:
308
- this.$ = 0;
317
+ currentOttava = 0; this.$ = 0;
309
318
  break;
310
319
  case 89:
311
320
  this.$ = 'up';
@@ -737,6 +746,7 @@ parse: function parse(input) {
737
746
  let currentTimeSig = null;
738
747
  let currentDuration = { division: 4, dots: 0 }; // default quarter note
739
748
  let numericTimeSignature = false; // When true, 4/4 and 2/2 use numeric display instead of C/C|
749
+ let currentOttava = 0; // Current ottava level, resets on newline
740
750
 
741
751
  // Reset parser state - call before each parse
742
752
  const resetParserState = () => {
@@ -745,6 +755,7 @@ parse: function parse(input) {
745
755
  currentTimeSig = null;
746
756
  currentDuration = { division: 4, dots: 0 };
747
757
  numericTimeSignature = false;
758
+ currentOttava = 0;
748
759
  };
749
760
 
750
761
  // Export reset function
package/lib/index.d.ts CHANGED
@@ -3,4 +3,5 @@ export * from "./parser";
3
3
  export * from "./serializer";
4
4
  import * as meiEncoder from "./meiEncoder";
5
5
  import * as musicXmlDecoder from "./musicXmlDecoder";
6
- export { meiEncoder, musicXmlDecoder, };
6
+ import * as lilypondEncoder from "./lilypondEncoder";
7
+ export { meiEncoder, musicXmlDecoder, lilypondEncoder, };
package/lib/index.js CHANGED
@@ -3,4 +3,5 @@ export * from "./parser.js";
3
3
  export * from "./serializer.js";
4
4
  import * as meiEncoder from "./meiEncoder.js";
5
5
  import * as musicXmlDecoder from "./musicXmlDecoder.js";
6
- export { meiEncoder, musicXmlDecoder, };
6
+ import * as lilypondEncoder from "./lilypondEncoder.js";
7
+ export { meiEncoder, musicXmlDecoder, lilypondEncoder, };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Lilylet to LilyPond Encoder
3
+ *
4
+ * Converts LilyletDoc to LilyPond (.ly) format.
5
+ * Uses relative pitch mode matching LilyPond's default behavior.
6
+ */
7
+ import { LilyletDoc } from "./types";
8
+ interface RenderOptions {
9
+ paper?: {
10
+ width?: number | string;
11
+ height?: number | string;
12
+ };
13
+ fontSize?: number;
14
+ withMIDI?: boolean;
15
+ autoBeaming?: boolean;
16
+ }
17
+ /**
18
+ * Encode a complete LilyletDoc to LilyPond format
19
+ */
20
+ export declare const encode: (doc: LilyletDoc, options?: RenderOptions) => string;
21
+ /**
22
+ * Encode LilyletDoc to minimal LilyPond (music content only, no headers)
23
+ */
24
+ export declare const encodeMinimal: (doc: LilyletDoc) => string;
25
+ declare const _default: {
26
+ encode: (doc: LilyletDoc, options?: RenderOptions) => string;
27
+ encodeMinimal: (doc: LilyletDoc) => string;
28
+ };
29
+ export default _default;