summaryrefslogtreecommitdiff
path: root/doc/Design.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/Design.md')
-rw-r--r--doc/Design.md56
1 files changed, 34 insertions, 22 deletions
diff --git a/doc/Design.md b/doc/Design.md
index f345d82..7c3d076 100644
--- a/doc/Design.md
+++ b/doc/Design.md
@@ -292,10 +292,10 @@ MML文字列
| 相対音量 | `v+` / `v-` (ppmck) | 0〜15へクランプして合成 |
| スイープ | `s`+数値,符号付き数値 (ppmck) | 2A03 Pulse sweepへ反映 |
| タイ | `^` (ppmck), `&` (両モード) | `^`はタイ、`&`はスラー |
-| リピート | `[`, `]` + 数値 | ppmckはASTへ保持してwarning、Pyxelは展開 |
+| リピート | `[`, `]` + 数値, `\|` | 両モードで構造検証。ppmckのリピート内`\|`は終端分岐 |
| エンベロープ/LFO | `@v`/`@@`/`@MP`/`@EP`/`@EN` (ppmck), `@ENV`/`@VIB`/`@GLI` (pyxel) | IR v2定義表を参照して合成 |
| 小節線 | `\|` | 視覚用、再生に影響しない |
-| トラック識別 | ppmck: `A`/`B`/`T`/`N`/`L`, pyxel: `数字:` | |
+| トラック識別 | ppmck: `A`/`B`/`T`/`N`/`L`, pyxel: `0:`〜`3:` | |
| ヘッダー | `#` で始まる行 (ppmck) | |
| コメント | `;` 以降行末 (ppmck) | |
| 拡張コマンド | `@ENV`/`@VIB`/`@GLI` (pyxel) | 空白区切りの数値引数をIR v2へ保持して合成 |
@@ -303,14 +303,16 @@ MML文字列
### 5.3 Parser(モード別)
-Parserの責務は**トークン列からASTを生成するのみ**。値範囲チェックやチャンネル適合性の判断は行わない。
+Parserの責務は**トークン列から構造的に妥当なASTを生成すること**である。必須token、トップレベル順序、リピート対応、空定義を検証する。値範囲チェックやチャンネル適合性の判断は行わない。構文errorが1件でもあればSemanticAnalyzerへ進まない。
#### ppmckパーサ
- トラックヘッダー `A`/`B`/`T`/`N`/`L` を認識
- `#` ヘッダーを認識
- 各コマンドをASTノードに変換
-- リピートの開始・終了をAST文として保持し、未対応warningを返す
+- トップレベル順序を`headers → global definitions → tracks`として検証
+- `L`を専用文法で解析し、`l`、休符、リピート、小節線以外を拒否
+- リピートの開始・終端分岐・終了を`RepeatStartStmt`/`RepeatBreakStmt`/`RepeatEndStmt`として保持し、対応、分岐重複、ネストを検証
- タイ `^` / スラー `&` の結合対象をASTで保持
- `@v`/`@@`/`@MP`/`@EP`/`@EN` エンベロープ定義 `{...|...}` と使用をASTで保持
@@ -318,7 +320,7 @@ Parserの責務は**トークン列からASTを生成するのみ**。値範囲ã
- トラックヘッダー `0:`〜`3:` を認識
- `@ENV`/`@VIB`/`@GLI` のパラメータをASTノードに保持
-- リピート `[...]N` の開始・終了をAST文として保持し、SemanticAnalyzerでネストを展開
+- リピート `[...]N` の対応とネストを検証し、開始・終了をAST文として保持
### 5.4 SemanticAnalyzer(モード別)
@@ -330,7 +332,7 @@ SemanticAnalyzerの責務は**ASTを検証・解釈してNoteSequence IRを生æˆ
- MIDI音番号変換
- tick計算
- タイ処理
-- リピート展開(pyxel)
+- ppmck/pyxel共通のトランザクション型リピート展開(各トラック最大100,000イベント)
- 値範囲チェック
- 音域チェック
@@ -338,11 +340,11 @@ SemanticAnalyzerの責務は**ASTを検証・解釈してNoteSequence IRを生æˆ
- Triangle音量無視
- Noise音高無視
-- 全体ループ `L` の処理
+- `L`の時間軸から全チャンネル共通`loop_start`を算出し、4音声チャンネルへ`RepeatEvent(repeat_count=0)`を追加
- `@` = デューティ比、`q` = クオンタイズ(gate_time = value / 8)として解釈
- `^` タイを前の音符の音長延長として解釈
-- `D`/`s`/`v+`/`v-`、各種エンベロープ/LFOをIRへ保持し `SEMANTIC_UNSUPPORTED_FEATURE` warningを返す
-- リピートなど合成未対応のASTに対するwarning
+- `D`/`s`/`v+`/`v-`、各種エンベロープ/LFOを値域・チャンネル検証後にIRへ保持
+- mckc型の`[前半|後半]N`を展開。回数省略時だけ2周期プレビューwarningを返す
#### pyxel固有
@@ -350,7 +352,7 @@ SemanticAnalyzerの責務は**ASTを検証・解釈してNoteSequence IRを生æˆ
- ゲートタイム `Q0-Q100` → `0.0-1.0`
- `@` コマンドのチャンネル適合性チェック
- `@ENV`/`@VIB`/`@GLI` のIR v2定義、選択、解除、合成
-- 無限リピートの2回打ち切りwarning
+- 無限リピートの2周期プレビューwarning
- `&` スラーを前の音符の音長延長として解釈
---
@@ -361,7 +363,7 @@ SemanticAnalyzerの責務は**ASTを検証・解釈してNoteSequence IRを生æˆ
- **モード共通**: ppmck/pyxel両方を1つのASTノード型セットで表現
- **位置情報**: 全ノードに `line`, `column` を保持
-- **構造保持**: リピート開始・終了とタイの結合対象を、意味解析可能な文列として保持
+- **構造保持**: リピート開始・終端分岐・終了とタイの結合対象を、意味解析可能な文列として保持
### 6.2 主要ASTノード
@@ -515,6 +517,10 @@ class RepeatStartStmt(ASTNode):
pass
@dataclass
+class RepeatBreakStmt(ASTNode):
+ pass # ppmckの現在のリピート階層に属する終端分岐
+
+@dataclass
class RepeatEndStmt(ASTNode):
count: int | None # None=無限
@@ -595,9 +601,10 @@ class ErrorPhase(Enum):
### 8.2 ppmckモード
```bnf
-<ppmck_mml> ::= <ppmck_header>* <ppmck_track>*
-<ppmck_track> ::= <track_header> <ppmck_statement>*
-<track_header>::= "A" | "B" | "T" | "N" | "L"
+<ppmck_mml> ::= <ppmck_header>* <global_definition>* <ppmck_track>*
+<ppmck_track> ::= <audio_track> | <loop_track>
+<audio_track> ::= ("A" | "B" | "T" | "N") <ppmck_statement>*
+<loop_track> ::= "L" (<length_cmd> | <rest> | <loop_repeat> | <bar>)*
<ppmck_statement>
::= <note> | <rest> | <octave_cmd> | <length_cmd>
| <volume_cmd> | <relative_volume_cmd> | <duty_cmd>
@@ -608,7 +615,10 @@ class ErrorPhase(Enum):
| <lfo_def> | <lfo_use> | <lfo_off>
| <pitch_env_def> | <pitch_env_use> | <pitch_env_off>
| <note_env_def> | <note_env_use> | <note_env_off>
- | <repeat_start> | <repeat_end> | <bar>
+ | <ppmck_repeat> | <bar>
+<ppmck_repeat>::= "[" <ppmck_statement>* ("|" <ppmck_statement>*)? "]" <number>?
+<loop_repeat> ::= "[" (<length_cmd> | <rest> | <loop_repeat>)*
+ ("|" (<length_cmd> | <rest> | <loop_repeat>)*)? "]" <number>?
```
### 8.3 pyxelモード
@@ -617,12 +627,13 @@ class ErrorPhase(Enum):
<pyxel_mml> ::= <pyxel_track>*
<pyxel_track> ::= <pyxel_track_header> <pyxel_statement>*
<pyxel_track_header>
- ::= <number> ":"
+ ::= "0:" | "1:" | "2:" | "3:"
<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>
+ | <pyxel_repeat> | <bar> | <ext_cmd>
+<pyxel_repeat>::= "[" <pyxel_statement>* "]" <number>?
```
---
@@ -651,7 +662,7 @@ NoteSequence IRを入力としてPulse1、Pulse2、Triangle、Noiseを合成しã
|---|---|---|
| **大文字/小文字** | 小文字 `c d e`(`D` ディチューンは大文字のみ) | 大文字 `C D E` |
| **コマンド** | `o` `l` `v` `v+`/`v-` `t` `@` `q` `^` `&` `D` `s` | `O` `L` `V` `T` `Q` `@` `K` `Y` `&` |
-| **チャンネル指定** | `A` `B` `T` `N`(文字) | `0:`〜`3:`(番号) |
+| **チャンネル指定** | `A` `B` `T` `N`、制御用`L` | `0:`〜`3:`(番号) |
| **Parser** | `parser_ppmck.py` | `parser_pyxel.py` |
| **SemanticAnalyzer** | `semantic_ppmck.py` | `semantic_pyxel.py` |
| **音量範囲** | 0-15(クラシックAPU準拠) | 0-127 |
@@ -660,7 +671,7 @@ NoteSequence IRを入力としてPulse1、Pulse2、Triangle、Noiseを合成しã
| **タイ/スラー** | `^` = タイ、`&` = スラー | `&` = スラー(タイなし) |
| **ディチューン** | `D-127`〜`D126`(セント) | `Y-127`〜`Y127`(セント) |
| **エンベロープ/LFO** | `@v`/`@@`/`@MP`/`@EP`/`@EN`(合成対応) | `@ENV`/`@VIB`/`@GLI`(合成対応) |
-| **リピート** | `L`(構文上のループトラック)、`[...]`(AST保持・warning、合成未反映) | `[...]`(回数指定、省略時は2回で打ち切り、ネスト対応) |
+| **リピート** | `L`からloop_startを算出。`[...]N`と`[前半\|後半]N`をネスト展開 | `[...]N`をネスト展開 |
---
@@ -675,6 +686,7 @@ NoteSequence IRを入力としてPulse1、Pulse2、Triangle、Noiseを合成しã
| 5 | pyxel `@` とトラック番号の矛盾 | トラック番号優先。`@` はPulse系のみデューティ比として有効 |
| 6 | Noise note_number → period マッピング | ppmck: `period=8, mode=0` 固定。pyxel: マッピング式適用 |
| 7 | 出力構成 | `YYYYMMDD_HHMMSS_mmm/output.wav`, `YYYYMMDD_HHMMSS_mmm/output.mml` |
+| 8 | リピート安全上限 | 各トラックの展開後イベント数は100,000件以下。超過時は展開前に意味error |
---
@@ -684,12 +696,12 @@ NoteSequence IRを入力としてPulse1、Pulse2、Triangle、Noiseを合成しã
|---|---|
| MCPツール | `compose_mml`(compose/validate/template) |
| Lexer | ppmck/pyxel 両モード対応、共通トークン定義、INVALIDトークン。ppmck向けに `@v`/`@@`/`@MP`/`@EP`/`@EN`/`^`/`D`/`s`/`v+`/`v-` トークンを追加 |
-| Parser | ppmck/pyxel 別Parser、AST生成。ppmckでエンベロープ定義 `{...\|...}` と各種IR保持コマンドをパース |
-| SemanticAnalyzer | ppmck/pyxel 別Analyzer、AST→IR変換。ppmckで `@`=duty、`q`=quantize、`^`=tie を解釈し、未対応コマンドをIR保持+warning |
+| Parser | ppmck/pyxel 別Parser。必須token、トップレベル順序、case、リピート対応を検証し、ppmckの`RepeatBreakStmt`と専用L文法を構築 |
+| SemanticAnalyzer | ppmck/pyxel別Analyzer、AST→IR変換。共通反復展開、事前イベント上限、値域→チャンネル→状態→IR順の検証、Lのloop_start算出を実装 |
| IR | NoteSequence、各種Event(Quantize/Detune/Sweep/RelativeVolume/VolumeEnvelope/DutyEnvelope/Lfo/PitchEnv/NoteEnv 等)、ErrorDetail(phase付き) |
| Synthesizer | Pulse/Triangle/Noise 4ch合成、線形ミキシング、WAV出力 |
| Templates | basic/melody/chord/drum/empty の5種 × 2モード。ppmckテンプレートは `@` でデューティ比を指定 |
-| テスト | pytestの固定・統合テスト、Hypothesisの生成テスト、ruffによるリント/フォーマット。全200テスト通過 |
+| テスト | pytestの固定・統合テスト、Hypothesisの生成テスト、ruffによるリント/フォーマット |
---