summaryrefslogtreecommitdiff
path: root/doc/Design.md
blob: 5dbe36bcf990ea5ed3fae7303e25f86e786196eb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
# レトロチップ音源作曲MCPサーバ — 統合設計書

> 作成日: 2026-07-11 | 更新日: 2026-07-12 | ステータス: **第2段階設計(パーサー再設計済み)**

---

## 1. 概要

### 1.1 目的

LLMがMML(Music Macro Language)を記述することで、レトロチップ音源(2A03 APU)のAPU音源をエミュレートした音楽を作曲・再生できるMCPサーバを設計・実装する。

### 1.2 設計目標

| 目標 | 内容 |
|---|---|
| **LLMフレンドリ** | LLMが生成しやすいMML構文、明確なエラーメッセージ |
| **2モード対応** | `ppmck`モード(クラシックAPU準拠)と`pyxel`モード(Pyxel準拠)を切り替え可能 |
| **正確な音源エミュレーション** | レトロAPU 4チャンネルの特性を忠実に再現 |
| **拡張性** | DPCM、エンベロープ等の第2段階機能を前提とした設計 |
| **単一ツール** | `compose_mml` の1ツールで作曲・検証・テンプレート生成を完結 |

### 1.3 スコープ

| 対象 | 内容 |
|---|---|
| 第1段階(実装済み) | 4ch MML解析、WAV合成、バリデーション、テンプレート生成、基本エラーコード |
| 第2段階(将来) | DPCMチャンネル、NSFファイル出力、クラシックAPU実機風非線形ミキシング、エンベロープ合成、区間ループ(ppmck) |
| 対象外 | GUI / Webフロントエンド |

### 1.4 技術スタック

| 項目 | 選定 | 理由 |
|---|---|---|
| 実装言語 | Python 3.14 | FastMCP対応、numpyで音声合成、LLM親和性 |
| MCP SDK | FastMCP >= 2.1.0 | 優良SDK |
| 音声合成 | numpy >= 2.0.0 | 波形生成の数値計算 |
| 音声出力 | 標準ライブラリ wave | WAV形式、外部依存なし |
| テスト | pytest, ruff | 単体テスト・リント・フォーマット |

---

## 2. システム全体アーキテクチャ

### 2.1 全体構成

```
┌──────────────────────────────────────────────────────────┐
│                     LLM Client (Claude等)                 │
│                  ┌──────────────────┐                    │
│                  │   compose_mml    │                    │
│                  └────────┬─────────┘                    │
└───────────────────────────┼─────────────────────────────┘
                            │
┌───────────────────────────┴─────────────────────────────┐
│                    MCP Server Layer                       │
│                                                            │
│  ┌──────────────────────────────────────────────────────┐ │
│  │                 Tool Dispatcher                       │ │
│  │   action パラメータで動作を切り替え                      │ │
│  │   (compose / validate / template)                     │ │
│  └──────────────────┬───────────────────────────────────┘ │
│                     │                                      │
│  ┌──────────────────┴───────────────────────────────────┐ │
│  │              MML Parser Pipeline                       │ │
│  │                                                        │ │
│  │  ┌─────────┐     ┌──────────────┐     ┌────────────┐ │ │
│  │  │  Lexer   │────▶│   Parser     │────▶│    AST     │ │ │
│  │  │ (共通)   │     │(ppmck/pyxel) │     │  (共通)    │ │ │
│  │  └─────────┘     └──────────────┘     └─────┬──────┘ │ │
│  │                                              │         │ │
│  │  ┌───────────────────────────────────────────┘         │ │
│  │  │           SemanticAnalyzer                         │ │
│  │  │     (ppmck/pyxel) ──▶ NoteSequence IR              │ │
│  │  └────────────────────────────────────────────────────│ │
│  └─────────────────────────────────────────────────────────┘ │
│                                                  │          │
│  ┌───────────────────────────────────────────────┴────────┐ │
│  │              APU Synthesis Engine                      │ │
│  │                                                        │ │
│  │  ┌────────┐  ┌────────┐  ┌──────────┐  ┌───────────┐  │ │
│  │  │ Pulse1 │  │ Pulse2 │  │ Triangle │  │  Noise    │  │ │
│  │  └───┬────┘  └───┬────┘  └────┬─────┘  └─────┬─────┘  │ │
│  │      └───────────┴───────────┴───────────────┘         │ │
│  │                    │                                   │ │
│  │              ┌─────┴─────┐                            │ │
│  │              │  Mixer     │                            │ │
│  │              │ (4ch→WAV)  │                            │ │
│  │              └───────────┘                            │ │
│  └────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘
```

### 2.2 処理パイプライン

```
MML文字列
  │
  ▼
[Lexer] ──トークン列──▶ [Parser] ──AST──▶ [SemanticAnalyzer] ──NoteSequence──▶ [Synthesizer] ──WAV
                           │                      │
                      構文エラー              意味エラー
                      (SyntaxError)          (SemanticError)
```

| ステップ | コンポーネント | 役割 | 検出するエラー |
|---|---|---|---|
| 1 | Lexer | MML文字列をトークン列に変換。両モード共通のトークン定義 | 不正文字(SyntaxError) |
| 2 | Parser | トークン列を構文解析し、ASTを生成。ppmckとpyxelで別のパーサー | 構文エラー(SyntaxError) |
| 3 | SemanticAnalyzer | ASTを検証・解釈し、NoteSequence(中間表現)を生成 | 意味エラー(SemanticError) |
| 4 | Synthesizer | NoteSequenceを入力として、numpyでAPU各チャンネルの波形を合成 | 実行時エラー(RuntimeError) |

---

## 3. MCPツール仕様

### 3.1 ツール一覧

| ツール名 | 機能 |
|---|---|
| `compose_mml` | MMLの作曲・コンパイル・検証・テンプレート生成を統合した単一ツール |

### 3.2 `compose_mml` 詳細仕様

#### パラメータ

| パラメータ | 型 | required | デフォルト | 説明 |
|---|---|---|---|---|
| `action` | string | yes | — | 動作モード: `"compose"` / `"validate"` / `"template"` |
| `mml` | string | compose/validate時 | `""` | MML文字列 |
| `mode` | string | compose/validate時 | `""` | `"ppmck"` / `"pyxel"` |
| `template` | string | template時 | `"basic"` | テンプレート種別: `"basic"` / `"melody"` / `"chord"` / `"drum"` / `"empty"` |
| `sample_rate` | int | no | `44100` | 出力サンプリング周波数(Hz) |
| `normalize` | bool | no | `true` | 出力振幅の正規化 |

#### action別の動作と戻り値

**`action: "compose"`** — MMLをコンパイルしてWAVを生成

```json
{
  "success": true,
  "wav_path": "./data/output_20260711_120000_123.wav",
  "duration_sec": 2.0,
  "note_sequence": { ... },
  "validation": {
    "errors": [],
    "warnings": []
  }
}
```

| フィールド | 型 | 説明 |
|---|---|---|
| `success` | boolean | 合成・WAV出力が成功したか |
| `wav_path` | string \| null | 生成されたWAVファイルパス(失敗時はnull) |
| `duration_sec` | number | 生成された音声の長さ(秒) |
| `note_sequence` | object \| null | 中間表現(IR)のNoteSequence |
| `validation.errors` | array | エラーのリスト |
| `validation.warnings` | array | 警告のリスト |

> `compose` は必ず検証を内包する。エラーがあればWAVは生成せず、検証結果のみ返す。
>
> **WAVファイル名**: 固定名 `output.wav` ではなく、生成時刻を含む `output_YYYYMMDD_HHMMSS_mmm.wav` 形式で出力される。

**`action: "validate"`** — MMLの構文チェックのみ(音声生成なし)

```json
{
  "valid": true,
  "errors": [],
  "warnings": [],
  "note_sequence": { ... },
  "channel_summary": [
    {
      "channel": "Pulse1",
      "note_count": 4,
      "octave_range": [4, 5],
      "duration_ticks": 768
    }
  ]
}
```

**`action: "template"`** — MMLテンプレートを生成

```json
{
  "mml": "0: T120 L8 O4 V100 @1\n   C D E F | ...",
  "description": "基本的な4ch構成(メロディ+和音+ベース+リズム)"
}
```

### 3.3 テンプレート種別

| 種別 | 内容 |
|---|---|
| `basic` | 基本的な4ch構成(メロディ+和音+ベース+リズム) |
| `melody` | メロディ重視(Pulse1主旋律、他は伴奏最小限) |
| `chord` | コード伴奏重視(Pulse2で和音、Triangleでベース) |
| `drum` | リズム重視(Noise中心のビートパターン) |
| `empty` | 各チャンネルのヘッダーのみ(空のテンプレート) |

---

## 4. 中間表現: NoteSequence IR

### 4.1 設計方針

| 項目 | 内容 |
|---|---|
| 共通IR | 両モード(ppmck / pyxel)のSemanticAnalyzerが共通で出力する |
| 後段分離 | 後段のSynthesizerはNoteSequenceのみを見れば動く(MMLの文法を知らない) |
| JSON互換 | JSON互換の構造体で表現し、MCPレスポンスに直接埋め込める |

### 4.2 時間解像度

```
1拍(4分音符) = 192 ticks
```

### 4.3 データ構造

```
NoteSequence = {
  version: "1.0",
  bpm: int,
  ticks_per_quarter: 192,
  channels: {
    "Pulse1":   ChannelSequence,
    "Pulse2":   ChannelSequence,
    "Triangle": ChannelSequence,
    "Noise":    ChannelSequence
  }
}

ChannelSequence = {
  channel_type: "pulse" | "triangle" | "noise",
  events: [Event, ...],
  total_ticks: int
}

Event = NoteEvent | RestEvent | VolumeEvent | DutyEvent
      | TempoEvent | RepeatEvent
      | EnvelopeEvent | VibratoEvent | GlideEvent
```

(各イベントのフィールドは第1段階実装と同じ)

---

## 5. MMLパーサ設計

### 5.1 新アーキテクチャ: 4フェーズ分離

```
MML文字列
  │
  ▼
[Lexer] ──トークン列──▶ [Parser] ──AST──▶ [SemanticAnalyzer] ──NoteSequence IR
                           │                      │
                      SyntaxError            SemanticError
```

| フェーズ | 入力 | 出力 | エラー |
|---|---|---|---|
| Lexer | MML文字列 | トークン列 | 不正文字(SyntaxError) |
| Parser | トークン列 | AST | 構文エラー(SyntaxError) |
| SemanticAnalyzer | AST | NoteSequence IR | 意味エラー(SemanticError) |

### 5.2 Lexer(共通)

両モードの字句解析を統一。モードごとに大文字/小文字を正規化した後にトークン化する。

#### 主なトークン種別

| カテゴリ | トークン | 備考 |
|---|---|---|
| 音符 | `c`-`b` (ppmck) / `C`-`B` (pyxel) | 大文字小文字問わず判定 |
| 休符 | `r` / `R` | |
| オクターブ | `o`/`O` + 数値, `>`, `<` | |
| 音長 | `l`/`L` + 数値, 音符直後の数値, `.` | |
| 音量 | `v`/`V` + 数値 | |
| デューティ/音色 | `q`+数値(ppmck), `@`+数値(pyxel) | |
| テンポ | `t`/`T` + 数値 | |
| ゲートタイム | `Q` + 数値 (pyxel) | |
| トランスポーズ | `K` + 数値 (pyxel) | |
| ディチューン | `Y` + 数値 (pyxel) | |
| タイ/スラー | `&` | |
| リピート | `[`, `]` + 数値 (pyxel) | |
| 小節線 | `\|` | 視覚用、再生に影響しない |
| トラック識別 | ppmck: `A`/`B`/`T`/`N`/`L`, pyxel: `数字:` | |
| ヘッダー | `#` で始まる行 (ppmck) | |
| コメント | `;` 以降行末 (ppmck) | |
| 拡張コマンド | `@ENV`/`@VIB`/`@GLI` (pyxel) | 第1段階は保持のみ |
| 不正文字 | `INVALID` | 新規: 構文エラーとして報告 |

### 5.3 Parser(モード別)

Parserの責務は**トークン列からASTを生成するのみ**。値範囲チェックやチャンネル適合性の判断は行わない。

#### ppmckパーサ

- トラックヘッダー `A`/`B`/`T`/`N`/`L` を認識
- `#` ヘッダーを認識
- 各コマンドをASTノードに変換
- リピート `[...]` のネスト構造をASTで保持
- タイ `&` の結合対象をASTで保持

#### pyxelパーサ

- トラックヘッダー `0:`〜`3:` を認識
- `@ENV`/`@VIB`/`@GLI` のパラメータをASTノードに保持
- リピート `[...]N` のネスト構造をASTで保持

### 5.4 SemanticAnalyzer(モード別)

SemanticAnalyzerの責務は**ASTを検証・解釈してNoteSequence IRを生成**する。

#### 共通処理

- 状態管理(`octave`, `velocity`, `duty`, `tick_position`)
- MIDI音番号変換
- tick計算
- タイ処理
- リピート展開(pyxel)
- 値範囲チェック
- 音域チェック

#### ppmck固有

- Triangle音量無視
- Noise音高無視
- 全体ループ `L` の処理
- 第2段階コマンドのwarning

#### pyxel固有

- 音量 `V0-V127` → `0-15` 正規化
- ゲートタイム `Q0-Q100` → `0.0-1.0`
- `@` コマンドのチャンネル適合性チェック
- `@ENV`/`@VIB`/`@GLI` のIR保持
- 無限リピートの2回打ち切りwarning

---

## 6. 抽象構文木(AST)

### 6.1 設計方針

- **モード共通**: ppmck/pyxel両方を1つのASTノード型セットで表現
- **位置情報**: 全ノードに `line`, `column` を保持
- **構造保持**: リピートのネスト、タイの結合関係等を保持

### 6.2 主要ASTノード

```python
@dataclass
class Program(ASTNode):
    tracks: list[Track]

@dataclass
class Track(ASTNode):
    track_id: str      # "A"/"B"/"T"/"N"/"L" or "0"-"3"
    channel: str       # "Pulse1"/"Pulse2"/"Triangle"/"Noise"/"Loop"
    mode: str          # "ppmck"/"pyxel"
    statements: list[Statement]
    headers: list[Header]

@dataclass
class NoteStmt(ASTNode):
    note_name: str     # "c"-"b"(正規化済み)
    accidental: int    # +1=sharp, -1=flat, 0=none
    length: int | None
    dots: int

@dataclass
class RestStmt(ASTNode):
    length: int | None
    dots: int

@dataclass
class OctaveStmt(ASTNode):
    value: int | None
    direction: str | None  # "up"/"down"

@dataclass
class LengthStmt(ASTNode):
    value: int

@dataclass
class VolumeStmt(ASTNode):
    value: int

@dataclass
class DutyStmt(ASTNode):
    value: int

@dataclass
class TempoStmt(ASTNode):
    value: int

@dataclass
class GateTimeStmt(ASTNode):
    value: int

@dataclass
class TransposeStmt(ASTNode):
    value: int

@dataclass
class DetuneStmt(ASTNode):
    value: int

@dataclass
class TieStmt(ASTNode):
    target: NoteStmt | RestStmt | int | None

@dataclass
class RepeatStartStmt(ASTNode):
    pass

@dataclass
class RepeatEndStmt(ASTNode):
    count: int | None  # None=無限

@dataclass
class BarStmt(ASTNode):
    pass

@dataclass
class ExtCmdStmt(ASTNode):
    cmd: str           # "ENV"/"VIB"/"GLI"
    slot: int
    params: list[int]
```

---

## 7. エラーハンドリング

### 7.1 3層エラー分類

```python
class ErrorPhase(Enum):
    LEXER = "lexer"
    SYNTAX = "syntax"
    SEMANTIC = "semantic"
    RUNTIME = "runtime"
    API = "api"
```

| 層 | コード接頭辞 | 発生フェーズ | 例 |
|---|---|---|---|
| **SyntaxError** | `SYNTAX_*` | Parser | 無効なトークン、未終端のリピート、不正なトラックヘッダー |
| **SemanticError** | `SEMANTIC_*` | SemanticAnalyzer | 値範囲外、チャンネル不適合、音域外、未定義参照 |
| **RuntimeError** | `RUNTIME_*` | Synthesizer | 音声合成失敗、WAV出力失敗、内部エラー |
| **APIError** | `VALIDATION_*` | Tool Dispatcher | パラメータ欠落、無効なmode/action |

### 7.2 ErrorDetail構造

```json
{
  "code": "SEMANTIC_VALUE_OUT_OF_RANGE",
  "phase": "semantic",
  "line": 1,
  "column": 5,
  "message": "'o' の値 9 は範囲外です。有効範囲: 0〜7。",
  "severity": "error",
  "hint": "例: o4 のように指定してください。",
  "context": "A t120 l8 o9 c"
}
```

### 7.3 新旧エラーコード対応

| 旧コード | 新コード | 理由 |
|---|---|---|
| `SYNTAX_VALUE_OUT_OF_RANGE` | `SEMANTIC_VALUE_OUT_OF_RANGE` | 値範囲は意味エラー |
| `SYNTAX_NOTE_OUT_OF_RANGE` | `SEMANTIC_NOTE_OUT_OF_RANGE` | 音域は意味エラー |
| `SYNTAX_CHANNEL_MISMATCH` | `SEMANTIC_CHANNEL_MISMATCH` | チャンネル適合性は意味エラー |
| `SYNTAX_EMPTY_TRACK` | `SEMANTIC_EMPTY_TRACK` | 空トラックは意味エラー |
| `SYNTAX_OUTSIDE_TRACK` | `SEMANTIC_OUTSIDE_TRACK` | トラック外コマンドは意味エラー |
| `SYNTAX_UNDEFINED_REFERENCE` | `SEMANTIC_UNDEFINED_REFERENCE` | 未定義参照は意味エラー |
| `SYSTEM_SYNTHESIS_FAILED` | `RUNTIME_SYNTHESIS_FAILED` | 実行時エラー |
| `SYSTEM_WAV_WRITE_FAILED` | `RUNTIME_WAV_WRITE_FAILED` | 実行時エラー |
| `SYSTEM_INTERNAL_ERROR` | `RUNTIME_INTERNAL_ERROR` | 実行時エラー |

### 7.4 回復戦略

| 状況 | 動作 |
|---|---|
| ERROR時 | そのフェーズで解析を停止し、エラーを返す |
| WARNING時 | 警告を記録しつつ、妥当な値にクランプして処理を継続 |
| compose action | ERRORがある場合WAVを生成せず検証結果のみ返す |

---

## 8. BNF文法(概要)

詳細は [plans/mml-parser-refactor.md](plans/mml-parser-refactor.md) 第3章を参照。

### 8.1 共通要素

```bnf
<note_name>   ::= "c" | "d" | "e" | "f" | "g" | "a" | "b"
<accidental>  ::= "+" | "#" | "-"
<length>      ::= <number> <dot>*
<octave_up>   ::= ">"
<octave_down> ::= "<"
<tie>         ::= "&"
```

### 8.2 ppmckモード

```bnf
<ppmck_mml>   ::= <ppmck_header>* <ppmck_track>*
<ppmck_track> ::= <track_header> <ppmck_statement>*
<track_header>::= "A" | "B" | "T" | "N" | "L"
<ppmck_statement>
              ::= <note> | <rest> | <octave_cmd> | <length_cmd>
                | <volume_cmd> | <duty_cmd> | <tempo_cmd> | <tie_cmd>
                | <repeat_start> | <repeat_end> | <bar>
```

### 8.3 pyxelモード

```bnf
<pyxel_mml>   ::= <pyxel_track>*
<pyxel_track> ::= <pyxel_track_header> <pyxel_statement>*
<pyxel_track_header>
              ::= <number> ":"
<pyxel_statement>
              ::= <note> | <rest> | <octave_cmd> | <length_cmd>
                | <volume_cmd> | <gate_cmd> | <tone_cmd> | <tempo_cmd>
                | <transpose_cmd> | <detune_cmd> | <tie_cmd>
                | <repeat_start> | <repeat_end> | <bar> | <ext_cmd>
```

---

## 9. APU音声合成エンジン

(第1段階実装と同じ。詳細は省略)

---

## 10. MMLコマンド仕様

(第1段階実装と同じ。詳細は省略)

---

## 11. 2モード比較サマリー

| 項目 | ppmckモード | pyxelモード |
|---|---|---|
| **大文字/小文字** | 小文字 `c d e` | 大文字 `C D E` |
| **コマンド** | `o` `l` `v` `t` `q` | `O` `L` `V` `T` `Q` `@` `K` `Y` |
| **チャンネル指定** | `A` `B` `T` `N`(文字) | `0:`〜`3:`(番号) |
| **Parser** | `parser_ppmck.py` | `parser_pyxel.py` |
| **SemanticAnalyzer** | `semantic_ppmck.py` | `semantic_pyxel.py` |
| **音量範囲** | 0-15(クラシックAPU準拠) | 0-127 |
| **デューティ比** | `q0`〜`q3` で4種 | `@1`(50%), `@2`(25%) の2種のみ |
| **リピート** | `L`(全体), `[...]`(第2段階) | `[...]`(回数指定、省略時無限、ネスト対応) |

---

## 12. 確定事項一覧

| # | 項目 | 決定内容 |
|---|---|---|
| 1 | パーサーアーキテクチャ | Lexer → Parser → SemanticAnalyzer → Synthesizer の4フェーズ分離 |
| 2 | AST | モード共通ASTノード型セットを採用 |
| 3 | エラー分類 | SyntaxError / SemanticError / RuntimeError / APIError の4層分離 |
| 4 | BNF | [plans/mml-parser-refactor.md](plans/mml-parser-refactor.md) に両モードのBNFを定義 |
| 5 | pyxel `@` とトラック番号の矛盾 | トラック番号優先。`@` はPulse系のみデューティ比として有効 |
| 6 | Noise note_number → period マッピング | ppmck: `period=8, mode=0` 固定。pyxel: マッピング式適用 |
| 7 | WAVファイル名 | `output_YYYYMMDD_HHMMSS_mmm.wav` |

---

## 13. 実装状態と今後の予定

### 13.1 第1段階実装済み機能

| カテゴリ | 実装内容 |
|---|---|
| MCPツール | `compose_mml`(compose/validate/template) |
| Lexer | ppmck/pyxel 両モード対応、共通トークン定義、INVALIDトークン |
| Parser | ppmck/pyxel 別Parser、AST生成 |
| SemanticAnalyzer | ppmck/pyxel 別Analyzer、AST→IR変換 |
| IR | NoteSequence、各種Event、ErrorDetail(phase付き) |
| Synthesizer | Pulse/Triangle/Noise 4ch合成、線形ミキシング、WAV出力 |
| Templates | basic/melody/chord/drum/empty の5種 × 2モード |
| テスト | pytestによる各種テスト、ruffによるリント/フォーマット |

### 13.2 第2段階予定機能

| カテゴリ | 予定内容 |
|---|---|
| DPCMチャンネル | 5ch目のDPCM対応 |
| NSF出力 | NSFファイル出力の検討 |
| 非線形ミキシング | クラシックAPU実機風ミキシングテーブル |
| エンベロープ合成 | `@v`(ppmck), `@ENV`(pyxel) の合成反映 |
| ppmck 区間リピート | `[...]` の展開 |
| ppmck スイープ | `s` コマンド |

---

以上が、レトロチップ音源作曲MCPサーバの統合設計書です。