diff options
Diffstat (limited to 'doc')
| -rw-r--r-- | doc/Design.md | 96 | ||||
| -rw-r--r-- | doc/MML_BNF.md | 23 |
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) を参照。 |
