diff options
| author | tanaka takashi <isazaka@gmail.com> | 2026-07-15 20:00:24 +0900 |
|---|---|---|
| committer | tanaka takashi <isazaka@gmail.com> | 2026-07-15 20:00:24 +0900 |
| commit | 7249ca3a6fdf51f6b7ea66ac6854edd5e02ae5ec (patch) | |
| tree | 37a42621228a6f7a87ef02ae08d4613b2151a1a5 /doc | |
| parent | 9cbb60cf68ab2a8bd9ccc0dca4fbd46f2ae9b928 (diff) | |
feat: MMLパーサーと意味解析を厳格化
- ppmckの終端分岐付きリピートとL制御トラックを実装
- case、構文構造、音長、エフェクト値域の検証を強化
- Pyxel拡張コマンドのarityと参照検証を追加
- リピート展開上限と構文・意味エラー時の合成停止を追加
- BNF、設計書、適合監査、回帰テストを更新
Diffstat (limited to 'doc')
| -rw-r--r-- | doc/Design.md | 56 | ||||
| -rw-r--r-- | doc/MML_BNF.md | 102 |
2 files changed, 99 insertions, 59 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によるリント/フォーマット | --- diff --git a/doc/MML_BNF.md b/doc/MML_BNF.md index 0c3f3b0..6093911 100644 --- a/doc/MML_BNF.md +++ b/doc/MML_BNF.md @@ -12,8 +12,9 @@ <digit> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" <number> ::= <digit>+ -/* 音符名(Lexer で正規化: ppmck は小文字、pyxel は大文字 → 内部は小文字) */ -<note_name> ::= "c" | "d" | "e" | "f" | "g" | "a" | "b" +/* 音符名(case-sensitive。受理後のASTでは小文字へ正規化) */ +<ppmck_note_name> ::= "c" | "d" | "e" | "f" | "g" | "a" | "b" +<pyxel_note_name> ::= "C" | "D" | "E" | "F" | "G" | "A" | "B" /* 変化記号 */ <sharp> ::= "+" | "#" @@ -43,6 +44,8 @@ <whitespace> ::= " " | "\t" | "\r" | "\n" ``` +`<whitespace>`は字句間の区切りであり、statementやASTノードではない。`#`はppmckの行頭ヘッダー開始、または両モードで音符名の直後に置くsharpとしてのみ有効である。 + --- ## 2. ppmck モード @@ -50,7 +53,7 @@ ### 2.1 全体構造 ```bnf -<ppmck_mml> ::= <ppmck_header>* <ppmck_track>* +<ppmck_mml> ::= <ppmck_header>* <ppmck_global_definition>* <ppmck_track>* <ppmck_header> ::= "#" <header_key> <header_value>? <header_key> ::= "TITLE" | "COMPOSER" | "PROGRAMER" | "OCTAVE-REV" @@ -58,15 +61,30 @@ | "EFFECT-INCLUDE" <header_value> ::= "\"" <string> "\"" -<ppmck_track> ::= <track_header> <ppmck_statement>* -<track_header> ::= "A" | "B" | "T" | "N" | "L" +<ppmck_global_definition> + ::= <vol_envelope_def> | <duty_envelope_def> | <lfo_def> + | <pitch_env_def> | <note_env_def> + +<ppmck_track> ::= <audio_track> | <loop_track> +<audio_track> ::= <audio_track_header> <ppmck_statement>* +<audio_track_header> + ::= "A" | "B" | "T" | "N" +<loop_track> ::= "L" <loop_statement>* +<loop_statement> ::= <length_cmd> | <ppmck_rest> | <loop_repeat> | <bar> +<loop_repeat_item>::= <length_cmd> | <ppmck_rest> | <loop_repeat> +<loop_repeat> ::= "[" <loop_repeat_item>* ("|" <loop_repeat_item>*)? "]" <number>? ``` +グローバル定義は全トラック共通である。既存入力との互換性のため、同じ定義を音声トラック内の`<ppmck_ext_cmd>`として置くこともできる。`L`は音声を生成しない制御トラックで、0回または1回だけ記述できる。 + ### 2.2 ステートメント ```bnf -<ppmck_statement> ::= <note> - | <rest> +<ppmck_statement> ::= <ppmck_simple_statement> | <ppmck_repeat> | <bar> + +<ppmck_simple_statement> + ::= <ppmck_note> + | <ppmck_rest> | <octave_cmd> | <length_cmd> | <volume_cmd> @@ -76,14 +94,10 @@ | <tie_cmd> | <slur_cmd> | <ppmck_ext_cmd> - | <repeat_start> - | <repeat_end> - | <bar> | <comment> - | <newline> -<note> ::= <note_name> <accidental>? <length> -<rest> ::= "r" <length> +<ppmck_note> ::= <ppmck_note_name> <accidental>? <length> +<ppmck_rest> ::= "r" <length> <octave_cmd> ::= "o" <number> /* o0 〜 o7 */ | <octave_up> @@ -96,12 +110,18 @@ <quantize_cmd> ::= "q" <number> /* q1 〜 q8 */ <tempo_cmd> ::= "t" <number> /* t1 〜 */ -<tie_cmd> ::= "^" (<note> | <number>) -<slur_cmd> ::= "&" (<note> | <rest> | <number>) +<tie_cmd> ::= "^" (<ppmck_note> | <number>) +<slur_cmd> ::= "&" (<ppmck_note> | <ppmck_rest> | <number>) <comment> ::= ";" <string_to_eol> + +<ppmck_repeat_item> + ::= <ppmck_simple_statement> | <ppmck_repeat> +<ppmck_repeat> ::= "[" <ppmck_repeat_item>* ("|" <ppmck_repeat_item>*)? "]" <number>? ``` +`[本体]N`は本体をN回再生する。`[前半|後半]N`は`前半+後半`をN−1回、最後に前半だけを再生する。`N=1`では前半だけとなる。`|`は現在のリピート階層で高々1個で、ネストした`|`は内側に所属する。リピート外の`|`は小節線である。回数省略は無限を表し、IRプレビューでは`前半+後半`を2回展開して`repeat_count=0`を保持する。 + ### 2.3 合成拡張コマンド ```bnf @@ -123,27 +143,27 @@ | <note_env_off> <relative_volume_cmd> - ::= "v+" <number>? /* 1 〜 15 */ - | "v-" <number>? /* 1 〜 15 */ + ::= "v+" <number>? /* 省略時1、1 〜 15 */ + | "v-" <number>? /* 省略時1、1 〜 15 */ <detune_cmd> ::= "D" <signed_number> /* -127 〜 126 */ <sweep_cmd> ::= "s" <number> "," <signed_number> -<vol_envelope_def>::= "@v" <number> "=" "{" <number_list> "|" <number_list>? "}" +<vol_envelope_def>::= "@v" <number> "=" "{" <number_list> ("|" <number_list>?)? "}" <vol_envelope_use>::= "@v" <number> -<duty_envelope_def>::= "@" <number> "=" "{" <number_list> "|" <number_list>? "}" +<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_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_def> ::= "@EN" <number> "=" "{" <signed_number_list> ("|" <signed_number_list>?)? "}" <note_env_use> ::= "EN" <number> <note_env_off> ::= "ENOF" @@ -158,13 +178,10 @@ ```bnf <ppmck_stage2> ::= <count_length> - | <section_repeat> | <transpose_cmd> | <noise_mode_cmd> -<count_length> ::= <note_name> <accidental>? "%" <number> - -<section_repeat> ::= "[" <ppmck_statement>+ "]" <number>? +<count_length> ::= <ppmck_note_name> <accidental>? "%" <number> <transpose_cmd> ::= "K" <number> <noise_mode_cmd> ::= "m" <number> @@ -182,14 +199,14 @@ <pyxel_track> ::= <pyxel_track_header> <pyxel_statement>* <pyxel_track_header> - ::= <number> ":" /* 0: 1: 2: 3: */ + ::= "0:" | "1:" | "2:" | "3:" ``` ### 3.2 ステートメント ```bnf -<pyxel_statement> ::= <note> - | <rest> +<pyxel_statement> ::= <pyxel_note> + | <pyxel_rest> | <octave_cmd> | <length_cmd> | <volume_cmd> @@ -199,14 +216,12 @@ | <transpose_cmd> | <detune_cmd> | <tie_cmd> - | <repeat_start> - | <repeat_end> + | <pyxel_repeat> | <bar> | <ext_cmd> - | <newline> -<note> ::= <note_name> <accidental>? <length> -<rest> ::= "R" <length> +<pyxel_note> ::= <pyxel_note_name> <accidental>? <length> +<pyxel_rest> ::= "R" <length> <octave_cmd> ::= "O" <number> /* O0 〜 O7 */ | <octave_up> @@ -221,24 +236,29 @@ <transpose_cmd> ::= "K" <signed_number> /* 半音単位 */ <detune_cmd> ::= "Y" <signed_number> /* セント単位 */ -<tie_cmd> ::= <tie> (<note> | <rest> | <number>) +<tie_cmd> ::= <tie> (<pyxel_note> | <pyxel_rest> | <number>) +<pyxel_repeat> ::= "[" <pyxel_statement>* "]" <number>? ``` +Pyxelリピートはネストできる。指定回数は1以上で、回数省略は無限を表す。IRプレビューでは本体を2回展開し、`repeat_count=0`を保持する。Pyxelの`|`は常に小節線であり、終端分岐ではない。 + ### 3.3 合成拡張コマンド ```bnf <ext_cmd> ::= <env_cmd> | <vib_cmd> | <gli_cmd> <env_cmd> ::= "@ENV" <number> <env_def>? -<env_def> ::= <number>+ +<env_def> ::= <number> <number> (<number> <number>)* <vib_cmd> ::= "@VIB" <number> <vib_def>? -<vib_def> ::= <number>+ +<vib_def> ::= <number> <number> <number> <gli_cmd> ::= "@GLI" <number> <gli_def>? -<gli_def> ::= <number>+ +<gli_def> ::= <number> <number> ``` +slot 0は解除専用で、パラメータを指定できない。slot 1以上ではパラメータ付きの初出を定義、パラメータなしを既定義slotの選択として扱う。Pyxel公式の波括弧、カンマ、負数、`*`を使う拡張形式は本受理文法に含めない。 + --- ## 4. トラック・チャンネル対応 @@ -259,6 +279,8 @@ |---|---|---|---|---| | `o` / `O` | 両方 | 0 〜 7 | 4 | | | `l` / `L` | 両方 | 1 〜 192 | 4 | | +| 音符・休符の明示音長 | 両方 | 1 〜 192 | 現在の`l`/`L` | | +| タイ対象の音長 | 両方 | 1 〜 192 | — | | | `v` | ppmck | 0 〜 15 | 15 | | | `V` | pyxel | 0 〜 127 | 100 | | | `v+` / `v-` | ppmck | 1 〜 15 | 1 | 相対音量(0〜15へクランプ) | @@ -274,8 +296,14 @@ | `@MP` / `MP` / `MPOF` | ppmck | 0 〜 255 | — | 三角LFO | | `@EP` / `EP` / `EPOF` | ppmck | 0 〜 255 | — | cent単位ピッチエンベロープ | | `@EN` / `EN` / `ENOF` | ppmck | 0 〜 255 | — | 絶対半音オフセット列 | +| ppmck envelope slot | ppmck | 0 〜 255 | — | 全定義・参照共通 | +| volume envelope要素 | ppmck | 0 〜 15 | — | | +| duty envelope要素 | ppmck | 0 〜 3 | — | | +| pitch/note envelope要素 | ppmck | -127 〜 126 | — | | +| `@MP`要素 | ppmck | delay 0〜255, period 1〜255, depth 0〜255, 第4値0 | — | | | `K` | pyxel | -127 〜 127 | 0 | トランスポーズ(半音) | | `Y` | pyxel | -127 〜 127 | 0 | ディチューン(セント) | +| リピート回数 | 両方 | 1以上、または省略(無限) | 省略 | 展開後は各トラック100,000イベント以下 | --- |
