summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
Diffstat (limited to 'doc')
-rw-r--r--doc/Design.md141
-rw-r--r--doc/MML_BNF.md113
2 files changed, 208 insertions, 46 deletions
diff --git a/doc/Design.md b/doc/Design.md
index 1ce8aeb..525a22e 100644
--- a/doc/Design.md
+++ b/doc/Design.md
@@ -25,7 +25,7 @@ LLMがMML(Music Macro Language)を記述することで、レトロチップ
| 対象 | 内容 |
|---|---|
| 実装範囲 | Pulse1 / Pulse2 / Triangle / Noiseの4ch MML解析、WAV合成、バリデーション、テンプレート生成 |
-| 構文拡張 | Pyxel `@ENV` / `@VIB` / `@GLI`をIRへ保持し、合成未対応のwarningを返す |
+| 構文拡張 | Pyxel `@ENV` / `@VIB` / `@GLI`、ppmck `@v` / `@@` / `@MP` / `@EP` / `@EN` / `D` / `s` / `v+` / `v-` / `^` をIRへ保持し、合成未対応のwarningを返す |
| 対象外 | GUI / Webフロントエンド、DPCM、NSF出力、未定義のppmck拡張構文 |
### 1.4 技術スタック
@@ -241,7 +241,10 @@ ChannelSequence = {
}
Event = NoteEvent | RestEvent | VolumeEvent | DutyEvent
- | TempoEvent | RepeatEvent
+ | TempoEvent | RepeatEvent | QuantizeEvent | DetuneEvent
+ | SweepEvent | RelativeVolumeEvent
+ | VolumeEnvelopeEvent | DutyEnvelopeEvent | LfoEvent
+ | PitchEnvEvent | NoteEnvEvent
| EnvelopeEvent | VibratoEvent | GlideEvent
```
@@ -281,13 +284,16 @@ MML文字列
| オクターブ | `o`/`O` + 数値, `>`, `<` | |
| 音長 | `l`/`L` + 数値, 音符直後の数値, `.` | |
| 音量 | `v`/`V` + 数値 | |
-| デューティ/音色 | `q`+数値(ppmck), `@`+数値(pyxel) | |
+| デューティ/音色 | `@`+数値(ppmck/pyxel), `q`+数値(ppmck クオンタイズ) | |
| テンポ | `t`/`T` + 数値 | |
-| ゲートタイム | `Q` + 数値 (pyxel) | |
+| ゲートタイム | `q`+数値(ppmck 1-8), `Q` + 数値 (pyxel 0-100) | |
| トランスポーズ | `K` + 符号付き整数 (pyxel) | 例: `K-12`, `K+7` |
-| ディチューン | `Y` + 符号付き整数 (pyxel) | 例: `Y-25`, `Y+10` |
-| タイ/スラー | `&` | |
+| ディチューン | `D` + 符号付き整数 (ppmck), `Y` + 符号付き整数 (pyxel) | 例: `D-10`, `Y-25` |
+| 相対音量 | `v+` / `v-` (ppmck) | IR保持のみ |
+| スイープ | `s`+数値,数値 (ppmck) | IR保持のみ |
+| タイ | `^` (ppmck), `&` (両モード) | `^`はタイ、`&`はスラー |
| リピート | `[`, `]` + 数値 | ppmckはASTへ保持してwarning、Pyxelは展開 |
+| エンベロープ/LFO | `@v`/`@@`/`@MP`/`@EP`/`@EN` (ppmck), `@ENV`/`@VIB`/`@GLI` (pyxel) | IR保持のみ、合成未対応warning |
| 小節線 | `\|` | 視覚用、再生に影響しない |
| トラック識別 | ppmck: `A`/`B`/`T`/`N`/`L`, pyxel: `数字:` | |
| ヘッダー | `#` で始まる行 (ppmck) | |
@@ -305,7 +311,8 @@ Parserの責務は**トークン列からASTを生成するのみ**。値範囲ã
- `#` ヘッダーを認識
- 各コマンドをASTノードに変換
- リピートの開始・終了をAST文として保持し、未対応warningを返す
-- タイ `&` の結合対象をASTで保持
+- タイ `^` / スラー `&` の結合対象をASTで保持
+- `@v`/`@@`/`@MP`/`@EP`/`@EN` エンベロープ定義 `{...|...}` と使用をASTで保持
#### pyxelパーサ
@@ -332,6 +339,9 @@ SemanticAnalyzerの責務は**ASTを検証・解釈してNoteSequence IRを生æˆ
- Triangle音量無視
- Noise音高無視
- 全体ループ `L` の処理
+- `@` = デューティ比、`q` = クオンタイズ(gate_time = value / 8)として解釈
+- `^` タイを前の音符の音長延長として解釈
+- `D`/`s`/`v+`/`v-`、各種エンベロープ/LFOをIRへ保持し `SEMANTIC_UNSUPPORTED_FEATURE` warningを返す
- リピートなど合成未対応のASTに対するwarning
#### pyxel固有
@@ -341,6 +351,7 @@ SemanticAnalyzerの責務は**ASTを検証・解釈してNoteSequence IRを生æˆ
- `@` コマンドのチャンネル適合性チェック
- `@ENV`/`@VIB`/`@GLI` のIR保持と合成未対応warning
- 無限リピートの2回打ち切りwarning
+- `&` スラーを前の音符の音長延長として解釈
---
@@ -417,6 +428,89 @@ class TieStmt(ASTNode):
target: NoteStmt | RestStmt | int | None
@dataclass
+class TieCmdStmt(ASTNode):
+ target: NoteStmt | int | None
+
+@dataclass
+class QuantizeStmt(ASTNode):
+ value: int
+
+@dataclass
+class DetuneCmdStmt(ASTNode):
+ value: int
+
+@dataclass
+class RelativeVolumeStmt(ASTNode):
+ up: bool
+ value: int | None
+
+@dataclass
+class SweepStmt(ASTNode):
+ x: int
+ y: int
+
+@dataclass
+class VolumeEnvelopeUseStmt(ASTNode):
+ slot: int
+
+@dataclass
+class VolumeEnvelopeDefStmt(ASTNode):
+ slot: int
+ values: list[int]
+ loop: list[int] | None
+
+@dataclass
+class DutyEnvelopeUseStmt(ASTNode):
+ slot: int
+
+@dataclass
+class DutyEnvelopeDefStmt(ASTNode):
+ slot: int
+ values: list[int]
+ loop: list[int] | None
+
+@dataclass
+class LfoUseStmt(ASTNode):
+ slot: int
+
+@dataclass
+class LfoDefStmt(ASTNode):
+ slot: int
+ params: list[int]
+
+@dataclass
+class LfoOffStmt(ASTNode):
+ pass
+
+@dataclass
+class PitchEnvUseStmt(ASTNode):
+ slot: int
+
+@dataclass
+class PitchEnvDefStmt(ASTNode):
+ slot: int
+ values: list[int]
+ loop: list[int] | None
+
+@dataclass
+class PitchEnvOffStmt(ASTNode):
+ pass
+
+@dataclass
+class NoteEnvUseStmt(ASTNode):
+ slot: int
+
+@dataclass
+class NoteEnvDefStmt(ASTNode):
+ slot: int
+ values: list[int]
+ loop: list[int] | None
+
+@dataclass
+class NoteEnvOffStmt(ASTNode):
+ pass
+
+@dataclass
class RepeatStartStmt(ASTNode):
pass
@@ -506,7 +600,14 @@ class ErrorPhase(Enum):
<track_header>::= "A" | "B" | "T" | "N" | "L"
<ppmck_statement>
::= <note> | <rest> | <octave_cmd> | <length_cmd>
- | <volume_cmd> | <duty_cmd> | <tempo_cmd> | <tie_cmd>
+ | <volume_cmd> | <relative_volume_cmd> | <duty_cmd>
+ | <quantize_cmd> | <tempo_cmd> | <tie_cmd> | <slur_cmd>
+ | <detune_cmd> | <sweep_cmd>
+ | <vol_envelope_def> | <vol_envelope_use>
+ | <duty_envelope_def> | <duty_envelope_use>
+ | <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>
```
@@ -530,7 +631,7 @@ class ErrorPhase(Enum):
BNFの各規則は Lexer → AST → IR/診断の一貫経路で検証する。`tests/test_mml_conformance.py`は、音符・休符・コマンド・トラック・ヘッダー・拡張コマンドについて正例、負例、境界値、位置情報を検査する。Hypothesisによる固定seedの生成テストで、有効音列のAST/IR保存と単一トークン破壊の非黙殺も確認する。
-ppmckの将来予約構文は現在の受理言語に含めない。Pyxel拡張は空白区切り引数だけを受理し、AST/IRへ保持したうえで`SEMANTIC_UNSUPPORTED_FEATURE`をwarningとして返す。
+ppmckの将来予約構文は現在の受理言語に含めない。ppmckの `@v`/`@@`/`@MP`/`@EP`/`@EN` および `D`/`s`/`v+`/`v-` は構文解析してIRへ保持し、合成未対応を `SEMANTIC_UNSUPPORTED_FEATURE` でwarningとして返す。Pyxel拡張も同様に空白区切り引数を受理し、AST/IRへ保持したうえで `SEMANTIC_UNSUPPORTED_FEATURE` warningを返す。
## 9. APU音声合成エンジン
@@ -548,13 +649,17 @@ NoteSequence IRを入力としてPulse1、Pulse2、Triangle、Noiseを合成しã
| 項目 | ppmckモード | pyxelモード |
|---|---|---|
-| **大文字/小文字** | 小文字 `c d e` | 大文字 `C D E` |
-| **コマンド** | `o` `l` `v` `t` `q` | `O` `L` `V` `T` `Q` `@` `K` `Y` |
+| **大文字/小文字** | 小文字 `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:`(番号) |
| **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種のみ |
+| **デューティ比** | `@0`〜`@3` で4種(Pulse系のみ) | `@0`〜`@3`(Pulse系のみ有効) |
+| **クオンタイズ/ゲート** | `q1`〜`q8`(gate_time = value / 8) | `Q0`〜`Q100`(%) |
+| **タイ/スラー** | `^` = タイ、`&` = スラー | `&` = スラー(タイなし) |
+| **ディチューン** | `D-127`〜`D126`(IR保持のみ) | `Y-127`〜`Y127`(セント) |
+| **エンベロープ/LFO** | `@v`/`@@`/`@MP`/`@EP`/`@EN`(IR保持のみ) | `@ENV`/`@VIB`/`@GLI`(IR保持のみ) |
| **リピート** | `L`(構文上のループトラック)、`[...]`(AST保持・warning、合成未反映) | `[...]`(回数指定、省略時は2回で打ち切り、ネスト対応) |
---
@@ -578,13 +683,13 @@ NoteSequence IRを入力としてPulse1、Pulse2、Triangle、Noiseを合成しã
| カテゴリ | 実装内容 |
|---|---|
| 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付き) |
+| 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 |
+| 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モード |
-| テスト | pytestの固定・統合テスト、Hypothesisの生成テスト、ruffによるリント/フォーマット |
+| Templates | basic/melody/chord/drum/empty の5種 × 2モード。ppmckテンプレートは `@` でデューティ比を指定 |
+| テスト | pytestの固定・統合テスト、Hypothesisの生成テスト、ruffによるリント/フォーマット。全200テスト通過 |
---
diff --git a/doc/MML_BNF.md b/doc/MML_BNF.md
index 599fd24..83a1281 100644
--- a/doc/MML_BNF.md
+++ b/doc/MML_BNF.md
@@ -71,8 +71,11 @@
| <length_cmd>
| <volume_cmd>
| <duty_cmd>
+ | <quantize_cmd>
| <tempo_cmd>
| <tie_cmd>
+ | <slur_cmd>
+ | <ppmck_ext_cmd>
| <repeat_start>
| <repeat_end>
| <bar>
@@ -88,45 +91,86 @@
<length_cmd> ::= "l" <number> /* l1 〜 l192 */
<volume_cmd> ::= "v" <number> /* v0 〜 v15 */
-<duty_cmd> ::= "q" <number> /* q0 〜 q3, Pulse系のみ */
+
+<duty_cmd> ::= "@" <number> /* @0 〜 @3, Pulse系のみ */
+<quantize_cmd> ::= "q" <number> /* q1 〜 q8 */
<tempo_cmd> ::= "t" <number> /* t1 〜 */
-<tie_cmd> ::= <tie> (<note> | <rest> | <number>)
+<tie_cmd> ::= "^" (<note> | <number>)
+<slur_cmd> ::= "&" (<note> | <rest> | <number>)
<comment> ::= ";" <string_to_eol>
```
-### 2.3 将来予定コマンド(現在の受理文法には含めない)
+### 2.3 拡張コマンド(第1段階: IR 保持のみ、合成無視)
+
+```bnf
+<ppmck_ext_cmd> ::= <relative_volume_cmd>
+ | <detune_cmd>
+ | <sweep_cmd>
+ | <vol_envelope_def>
+ | <vol_envelope_use>
+ | <duty_envelope_def>
+ | <duty_envelope_use>
+ | <lfo_def>
+ | <lfo_use>
+ | <lfo_off>
+ | <pitch_env_def>
+ | <pitch_env_use>
+ | <pitch_env_off>
+ | <note_env_def>
+ | <note_env_use>
+ | <note_env_off>
+
+<relative_volume_cmd>
+ ::= "v+" <number>? /* 1 〜 15 */
+ | "v-" <number>? /* 1 〜 15 */
+
+<detune_cmd> ::= "D" <signed_number> /* -127 〜 126 */
+<sweep_cmd> ::= "s" <number> "," <number>
+
+<vol_envelope_def>::= "@v" <number> "=" "{" <number_list> "|" <number_list>? "}"
+<vol_envelope_use>::= "@v" <number>
+
+<duty_envelope_def>::= "@" <number> "=" "{" <number_list> "|" <number_list>? "}"
+<duty_envelope_use>::= "@@" <number>
+
+<lfo_def> ::= "@MP" <number> "=" "{" <number> "," <number> "," <number> "," <number> "}"
+<lfo_use> ::= "MP" <number>
+<lfo_off> ::= "MPOF"
+
+<pitch_env_def> ::= "@EP" <number> "=" "{" <signed_number_list> "|" <signed_number_list>? "}"
+<pitch_env_use> ::= "EP" <number>
+<pitch_env_off> ::= "EPOF"
+
+<note_env_def> ::= "@EN" <number> "=" "{" <signed_number_list> "|" <signed_number_list>? "}"
+<note_env_use> ::= "EN" <number>
+<note_env_off> ::= "ENOF"
+
+<signed_number> ::= ("+" | "-")? <number>
+<number_list> ::= <number> (","? <number>)*
+<signed_number_list> ::= <signed_number> (","? <signed_number>)*
+```
+
+### 2.4 将来予定コマンド(現在の受理文法には含めない)
以下は設計予約であり、現在は構文エラーになる。実装時に受理文法へ移す。
```bnf
<ppmck_stage2> ::= <count_length>
- | <envelope_def>
- | <envelope_use>
- | <duty_envelope_def>
- | <sweep_cmd>
| <section_repeat>
| <transpose_cmd>
| <noise_mode_cmd>
<count_length> ::= <note_name> <accidental>? "%" <number>
-<envelope_def> ::= "@v" <number> "=" "{" <number_list> "|" <number_list>? "}"
-<envelope_use> ::= "@v" <number>
-
-<duty_envelope_def>::= "@" <number> "=" "{" <number_list> "|" <number_list>? "}"
-
-<sweep_cmd> ::= "s" <number> "," <number>
-
<section_repeat> ::= "[" <ppmck_statement>+ "]" <number>?
<transpose_cmd> ::= "K" <number>
<noise_mode_cmd> ::= "m" <number>
-
-<number_list> ::= <number> (","? <number>)*
```
+
---
## 3. pyxel モード
@@ -211,16 +255,27 @@
## 5. 値範囲
-| コマンド | 有効範囲 | デフォルト |
-|---|---|---|
-| `o` / `O` | 0 〜 7 | 4 |
-| `l` / `L` | 1 〜 192 | 4 |
-| `v` (ppmck) | 0 〜 15 | 15 |
-| `V` (pyxel) | 0 〜 127 | 100 |
-| `q` (ppmck) | 0 〜 3 | 2 |
-| `@` (pyxel) | 0 〜 3 | — |
-| `Q` (pyxel) | 0 〜 100 | 80 |
-| `t` / `T` | 1 〜 | 120 |
+| コマンド | モード | 有効範囲 | デフォルト | 備考 |
+|---|---|---|---|---|
+| `o` / `O` | 両方 | 0 〜 7 | 4 | |
+| `l` / `L` | 両方 | 1 〜 192 | 4 | |
+| `v` | ppmck | 0 〜 15 | 15 | |
+| `V` | pyxel | 0 〜 127 | 100 | |
+| `v+` / `v-` | ppmck | 1 〜 15 | 1 | 相対音量(IR保持のみ) |
+| `@` | ppmck | 0 〜 3 | 2 | デューティ比(Pulse系のみ) |
+| `@` | pyxel | 0 〜 3 | — | デューティ比(Pulse系のみ) |
+| `q` | ppmck | 1 〜 8 | 8 | クオンタイズ(gate_time = value / 8) |
+| `Q` | pyxel | 0 〜 100 | 80 | ゲートタイム(%) |
+| `t` / `T` | 両方 | 1 〜 | 120 | |
+| `D` | ppmck | -127 〜 126 | 0 | ディチューン(IR保持のみ) |
+| `s` | ppmck | 0 〜 15, 0 〜 7 | — | スイープ `s<x>,<y>`(IR保持のみ) |
+| `@v` | ppmck | 0 〜 255 | — | 音量エンベロープ(IR保持のみ) |
+| `@@` | ppmck | 0 〜 255 | — | デューティエンベロープ(IR保持のみ) |
+| `@MP` / `MP` / `MPOF` | ppmck | 0 〜 255 | — | LFO(IR保持のみ) |
+| `@EP` / `EP` / `EPOF` | ppmck | 0 〜 255 | — | ピッチエンベロープ(IR保持のみ) |
+| `@EN` / `EN` / `ENOF` | ppmck | 0 〜 255 | — | ノートエンベロープ(IR保持のみ) |
+| `K` | pyxel | -127 〜 127 | 0 | トランスポーズ(半音) |
+| `Y` | pyxel | -127 〜 127 | 0 | ディチューン(セント) |
---
@@ -228,6 +283,8 @@
- 本 BNF は `mml-composemusic-mcp` の**サポート範囲**を定義するものであり、オリジナルの PPMCK や Pyxel の全機能を網羅するものではない。
- 第2段階予定コマンドは設計予約として記載するが、現在の受理文法には含めない。
-- `@ENV` / `@VIB` / `@GLI` は構文解析してIRへ保持し、合成未対応を `SEMANTIC_UNSUPPORTED_FEATURE` で警告する。
-- コマンド文字は ppmck では小文字、pyxel では大文字を使用する。音符・休符も同じ規則に従う。
+- ppmck モードの `@v` / `@@` / `@MP` / `@EP` / `@EN`、および `D` / `s` / `v+` / `v-` は構文解析して IR へ保持し、合成には反映しない。未対応を `SEMANTIC_UNSUPPORTED_FEATURE` で警告する。
+- pyxel モードの `@ENV` / `@VIB` / `@GLI` も同様に IR 保持のみで、`SEMANTIC_UNSUPPORTED_FEATURE` 警告を返す。
+- コマンド文字は ppmck では小文字、pyxel では大文字を使用する。音符・休符も同じ規則に従う。ただし ppmck の `D`(ディチューン)は大文字のみ受理する(小文字 `d` は音符)。
+- `^` は ppmck モード専用のタイ、`&` は両モード共通のスラー(slur)として扱う。
- 詳細な実装アーキテクチャは [Design.md](Design.md) を参照。