summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
Diffstat (limited to 'doc')
-rw-r--r--doc/Design.md96
-rw-r--r--doc/MML_BNF.md23
2 files changed, 50 insertions, 69 deletions
diff --git a/doc/Design.md b/doc/Design.md
index 5dbe36b..1ce8aeb 100644
--- a/doc/Design.md
+++ b/doc/Design.md
@@ -1,6 +1,6 @@
# レトロチップ音源作曲MCPサーバ — 統合設計書
-> 作成日: 2026-07-11 | 更新日: 2026-07-12 | ステータス: **第2段階設計(パーサー再設計済み)**
+> 作成日: 2026-07-11 | 更新日: 2026-07-12 | ステータス: **現行実装準拠**
---
@@ -17,16 +17,16 @@ LLMがMML(Music Macro Language)を記述することで、レトロチップ
| **LLMフレンドリ** | LLMが生成しやすいMML構文、明確なエラーメッセージ |
| **2モード対応** | `ppmck`モード(クラシックAPU準拠)と`pyxel`モード(Pyxel準拠)を切り替え可能 |
| **正確な音源エミュレーション** | レトロAPU 4チャンネルの特性を忠実に再現 |
-| **拡張性** | DPCM、エンベロープ等の第2段階機能を前提とした設計 |
+| **拡張性** | 共通AST・IRによりモード別の構文と音声合成を分離 |
| **単一ツール** | `compose_mml` の1ツールで作曲・検証・テンプレート生成を完結 |
### 1.3 スコープ
| 対象 | 内容 |
|---|---|
-| 第1段階(実装済み) | 4ch MML解析、WAV合成、バリデーション、テンプレート生成、基本エラーコード |
-| 第2段階(将来) | DPCMチャンネル、NSFファイル出力、クラシックAPU実機風非線形ミキシング、エンベロープ合成、区間ループ(ppmck) |
-| 対象外 | GUI / Webフロントエンド |
+| 実装範囲 | Pulse1 / Pulse2 / Triangle / Noiseの4ch MML解析、WAV合成、バリデーション、テンプレート生成 |
+| 構文拡張 | Pyxel `@ENV` / `@VIB` / `@GLI`をIRへ保持し、合成未対応のwarningを返す |
+| 対象外 | GUI / Webフロントエンド、DPCM、NSF出力、未定義のppmck拡張構文 |
### 1.4 技術スタック
@@ -36,7 +36,7 @@ LLMがMML(Music Macro Language)を記述することで、レトロチップ
| MCP SDK | FastMCP >= 2.1.0 | 優良SDK |
| 音声合成 | numpy >= 2.0.0 | 波形生成の数値計算 |
| 音声出力 | 標準ライブラリ wave | WAV形式、外部依存なし |
-| テスト | pytest, ruff | 単体テスト・リント・フォーマット |
+| テスト | pytest, Hypothesis, ruff | 単体・生成テスト、リント、フォーマット |
---
@@ -245,13 +245,13 @@ Event = NoteEvent | RestEvent | VolumeEvent | DutyEvent
| EnvelopeEvent | VibratoEvent | GlideEvent
```
-(各イベントのフィールドは第1段階実装と同じ)
+各イベントは位置・音長・音高・音量など、合成とMCPレスポンスに必要な値を保持する。
---
## 5. MMLパーサ設計
-### 5.1 新アーキテクチャ: 4フェーズ分離
+### 5.1 4フェーズ分離
```
MML文字列
@@ -270,13 +270,13 @@ MML文字列
### 5.2 Lexer(共通)
-両モードの字句解析を統一。モードごとに大文字/小文字を正規化した後にトークン化する。
+両モードでトークン型を共有し、モード別の字句規則でトークン化する。ppmckは小文字、Pyxelは大文字のコマンドを受理し、音名だけAST生成時に小文字へ正規化する。
#### 主なトークン種別
| カテゴリ | トークン | 備考 |
|---|---|---|
-| 音符 | `c`-`b` (ppmck) / `C`-`B` (pyxel) | 大文字小文字問わず判定 |
+| 音符 | `c`-`b` (ppmck) / `C`-`B` (pyxel) | `+` / `#` / `-` は音名直後、音長より前 |
| 休符 | `r` / `R` | |
| オクターブ | `o`/`O` + 数値, `>`, `<` | |
| 音長 | `l`/`L` + 数値, 音符直後の数値, `.` | |
@@ -284,16 +284,16 @@ MML文字列
| デューティ/音色 | `q`+数値(ppmck), `@`+数値(pyxel) | |
| テンポ | `t`/`T` + 数値 | |
| ゲートタイム | `Q` + 数値 (pyxel) | |
-| トランスポーズ | `K` + 数値 (pyxel) | |
-| ディチューン | `Y` + 数値 (pyxel) | |
+| トランスポーズ | `K` + 符号付き整数 (pyxel) | 例: `K-12`, `K+7` |
+| ディチューン | `Y` + 符号付き整数 (pyxel) | 例: `Y-25`, `Y+10` |
| タイ/スラー | `&` | |
-| リピート | `[`, `]` + 数値 (pyxel) | |
+| リピート | `[`, `]` + 数値 | ppmckはASTへ保持してwarning、Pyxelは展開 |
| 小節線 | `\|` | 視覚用、再生に影響しない |
| トラック識別 | ppmck: `A`/`B`/`T`/`N`/`L`, pyxel: `数字:` | |
| ヘッダー | `#` で始まる行 (ppmck) | |
| コメント | `;` 以降行末 (ppmck) | |
-| 拡張コマンド | `@ENV`/`@VIB`/`@GLI` (pyxel) | 第1段階は保持のみ |
-| 不正文字 | `INVALID` | 新規: 構文エラーとして報告 |
+| 拡張コマンド | `@ENV`/`@VIB`/`@GLI` (pyxel) | 空白区切りの数値引数をIRへ保持、合成未対応warning |
+| 不正文字 | `INVALID` | Parserが構文エラーとして報告 |
### 5.3 Parser(モード別)
@@ -304,14 +304,14 @@ Parserの責務は**トークン列からASTを生成するのみ**。値範囲ã
- トラックヘッダー `A`/`B`/`T`/`N`/`L` を認識
- `#` ヘッダーを認識
- 各コマンドをASTノードに変換
-- リピート `[...]` のネスト構造をASTで保持
+- リピートの開始・終了をAST文として保持し、未対応warningを返す
- タイ `&` の結合対象をASTで保持
#### pyxelパーサ
- トラックヘッダー `0:`〜`3:` を認識
- `@ENV`/`@VIB`/`@GLI` のパラメータをASTノードに保持
-- リピート `[...]N` のネスト構造をASTで保持
+- リピート `[...]N` の開始・終了をAST文として保持し、SemanticAnalyzerでネストを展開
### 5.4 SemanticAnalyzer(モード別)
@@ -332,14 +332,14 @@ SemanticAnalyzerの責務は**ASTを検証・解釈してNoteSequence IRを生æˆ
- Triangle音量無視
- Noise音高無視
- 全体ループ `L` の処理
-- 第2段階コマンドのwarning
+- リピートなど合成未対応のASTに対するwarning
#### pyxel固有
- 音量 `V0-V127` → `0-15` 正規化
- ゲートタイム `Q0-Q100` → `0.0-1.0`
- `@` コマンドのチャンネル適合性チェック
-- `@ENV`/`@VIB`/`@GLI` のIR保持
+- `@ENV`/`@VIB`/`@GLI` のIR保持と合成未対応warning
- 無限リピートの2回打ち切りwarning
---
@@ -350,7 +350,7 @@ SemanticAnalyzerの責務は**ASTを検証・解釈してNoteSequence IRを生æˆ
- **モード共通**: ppmck/pyxel両方を1つのASTノード型セットで表現
- **位置情報**: 全ノードに `line`, `column` を保持
-- **構造保持**: リピートのネスト、タイの結合関係等を保持
+- **構造保持**: リピート開始・終了とタイの結合対象を、意味解析可能な文列として保持
### 6.2 主要ASTノード
@@ -439,7 +439,7 @@ class ExtCmdStmt(ASTNode):
## 7. エラーハンドリング
-### 7.1 3層エラー分類
+### 7.1 エラー分類
```python
class ErrorPhase(Enum):
@@ -472,33 +472,20 @@ class ErrorPhase(Enum):
}
```
-### 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 回復戦略
+### 7.3 回復戦略
| 状況 | 動作 |
|---|---|
-| ERROR時 | そのフェーズで解析を停止し、エラーを返す |
-| WARNING時 | 警告を記録しつつ、妥当な値にクランプして処理を継続 |
+| Parser ERROR時 | 同一フェーズ内で回復可能な位置まで読み進めて複数の診断を収集し、意味解析へは進まない |
+| Semantic ERROR時 | 診断を収集し、不正な状態更新を行わず後続ASTの検証を継続 |
+| WARNING時 | 警告を記録し、仕様で定めた無視・クランプ・有限回展開などの代替動作で継続 |
| compose action | ERRORがある場合WAVを生成せず検証結果のみ返す |
---
## 8. BNF文法(概要)
-詳細は [plans/mml-parser-refactor.md](plans/mml-parser-refactor.md) 第3章を参照。
+受理言語の正本は [MML_BNF.md](MML_BNF.md) とする。値を要求するコマンドで数値を省略した場合は既定値へ置換せず、`SYNTAX_INVALID_NUMBER`を返す。全トラックIDはモード内で一度だけ定義でき、ppmckヘッダーは既知のキーを用いてトラックより前に置き、値を指定する場合は二重引用符で囲む。
### 8.1 共通要素
@@ -539,15 +526,21 @@ class ErrorPhase(Enum):
---
+### 8.4 文法適合性の保証
+
+BNFの各規則は Lexer → AST → IR/診断の一貫経路で検証する。`tests/test_mml_conformance.py`は、音符・休符・コマンド・トラック・ヘッダー・拡張コマンドについて正例、負例、境界値、位置情報を検査する。Hypothesisによる固定seedの生成テストで、有効音列のAST/IR保存と単一トークン破壊の非黙殺も確認する。
+
+ppmckの将来予約構文は現在の受理言語に含めない。Pyxel拡張は空白区切り引数だけを受理し、AST/IRへ保持したうえで`SEMANTIC_UNSUPPORTED_FEATURE`をwarningとして返す。
+
## 9. APU音声合成エンジン
-(第1段階実装と同じ。詳細は省略)
+NoteSequence IRを入力としてPulse1、Pulse2、Triangle、Noiseを合成し、線形ミキシングと任意の正規化を経てWAVへ出力する。
---
## 10. MMLコマンド仕様
-(第1段階実装と同じ。詳細は省略)
+コマンド、値範囲、デフォルト値の完全な定義は [MML_BNF.md](MML_BNF.md) を参照する。設計書と実装で差異が生じた場合はBNFを受理言語の基準として監査し、実装変更と同時に適合テストを更新する。
---
@@ -562,7 +555,7 @@ class ErrorPhase(Enum):
| **SemanticAnalyzer** | `semantic_ppmck.py` | `semantic_pyxel.py` |
| **音量範囲** | 0-15(クラシックAPU準拠) | 0-127 |
| **デューティ比** | `q0`〜`q3` で4種 | `@1`(50%), `@2`(25%) の2種のみ |
-| **リピート** | `L`(全体), `[...]`(第2段階) | `[...]`(回数指定、省略時無限、ネスト対応) |
+| **リピート** | `L`(構文上のループトラック)、`[...]`(AST保持・warning、合成未反映) | `[...]`(回数指定、省略時は2回で打ち切り、ネスト対応) |
---
@@ -573,16 +566,14 @@ class ErrorPhase(Enum):
| 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を定義 |
+| 4 | BNF | [MML_BNF.md](MML_BNF.md) を受理言語の正本とし、適合テストで実装との一致を保証 |
| 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段階実装済み機能
+## 13. 実装状態
| カテゴリ | 実装内容 |
|---|---|
@@ -593,18 +584,7 @@ class ErrorPhase(Enum):
| 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` コマンド |
+| テスト | pytestの固定・統合テスト、Hypothesisの生成テスト、ruffによるリント/フォーマット |
---
diff --git a/doc/MML_BNF.md b/doc/MML_BNF.md
index 3657ecd..599fd24 100644
--- a/doc/MML_BNF.md
+++ b/doc/MML_BNF.md
@@ -60,7 +60,6 @@
<ppmck_track> ::= <track_header> <ppmck_statement>*
<track_header> ::= "A" | "B" | "T" | "N" | "L"
- | /* 第2段階: 複合ヘッダー "ACE" 等 */
```
### 2.2 ステートメント
@@ -97,7 +96,9 @@
<comment> ::= ";" <string_to_eol>
```
-### 2.3 第2段階予定コマンド(未実装、BNF に含める)
+### 2.3 将来予定コマンド(現在の受理文法には含めない)
+
+以下は設計予約であり、現在は構文エラーになる。実装時に受理文法へ移す。
```bnf
<ppmck_stage2> ::= <count_length>
@@ -172,8 +173,9 @@
<gate_cmd> ::= "Q" <number> /* Q0 〜 Q100 */
<tone_cmd> ::= "@" <number> /* @0 〜 @3, Pulse系のみ有効 */
<tempo_cmd> ::= "T" <number> /* T1 〜 */
-<transpose_cmd> ::= "K" <number> /* 半音単位 */
-<detune_cmd> ::= "Y" <number> /* セント単位 */
+<signed_number> ::= ("+" | "-")? <number>
+<transpose_cmd> ::= "K" <signed_number> /* 半音単位 */
+<detune_cmd> ::= "Y" <signed_number> /* セント単位 */
<tie_cmd> ::= <tie> (<note> | <rest> | <number>)
```
@@ -184,16 +186,13 @@
<ext_cmd> ::= <env_cmd> | <vib_cmd> | <gli_cmd>
<env_cmd> ::= "@ENV" <number> <env_def>?
-<env_def> ::= "{" <number> "," <number> ("," <number>)* "}"
- | <number>+
+<env_def> ::= <number>+
<vib_cmd> ::= "@VIB" <number> <vib_def>?
-<vib_def> ::= "{" <number> "," <number> "," <number> "}"
- | <number>+
+<vib_def> ::= <number>+
<gli_cmd> ::= "@GLI" <number> <gli_def>?
-<gli_def> ::= "{" <number> "," <number> "}"
- | <number>+
+<gli_def> ::= <number>+
```
---
@@ -228,5 +227,7 @@
## 6. 備考
- 本 BNF は `mml-composemusic-mcp` の**サポート範囲**を定義するものであり、オリジナルの PPMCK や Pyxel の全機能を網羅するものではない。
-- 第2段階予定コマンドは BNF に含めるが、第1段階実装では `SEMANTIC_UNSUPPORTED_FEATURE` として警告を返す。
+- 第2段階予定コマンドは設計予約として記載するが、現在の受理文法には含めない。
+- `@ENV` / `@VIB` / `@GLI` は構文解析してIRへ保持し、合成未対応を `SEMANTIC_UNSUPPORTED_FEATURE` で警告する。
+- コマンド文字は ppmck では小文字、pyxel では大文字を使用する。音符・休符も同じ規則に従う。
- 詳細な実装アーキテクチャは [Design.md](Design.md) を参照。