summaryrefslogtreecommitdiff
path: root/src/mml_composemusic_mcp/ir.py
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-07-12 17:58:19 +0900
committertanaka takashi <isazaka@gmail.com>2026-07-12 17:59:16 +0900
commitbf36a773918959eeb6ffe7dae2fb8b0874438eb4 (patch)
treeda7ff0037ea923fea255d2ed69ed2fb51395d9b9 /src/mml_composemusic_mcp/ir.py
parentf1cd4d7d16f6e75a50bb0c587c0f759f7cf710cd (diff)
feat(ppmck): mckc準拠の構文拡張とIR保持コマンド追加
- @をデューティ比、qをクオンタイズに修正(mckc原本準拠) - ^タイ、Dディチューン、sスイープ、v+/v-相対音量を追加 - @v/@@/@MP/@EP/@EN エンベロープ・LFO系を構文解析してIR保持 - 上記IR保持コマンドは合成未対応を SEMANTIC_UNSUPPORTED_FEATURE で警告 - Design.md / MML_BNF.md を更新 - 全200テスト + ruff チェック通過
Diffstat (limited to 'src/mml_composemusic_mcp/ir.py')
-rw-r--r--src/mml_composemusic_mcp/ir.py117
1 files changed, 114 insertions, 3 deletions
diff --git a/src/mml_composemusic_mcp/ir.py b/src/mml_composemusic_mcp/ir.py
index 8d1bc25..4dd7978 100644
--- a/src/mml_composemusic_mcp/ir.py
+++ b/src/mml_composemusic_mcp/ir.py
@@ -134,6 +134,35 @@ class RepeatEvent:
@dataclass
+class QuantizeEvent:
+ type: str = "quantize"
+ tick_position: int = 0
+ value: int = 8 # 1-8
+
+
+@dataclass
+class DetuneEvent:
+ type: str = "detune"
+ tick_position: int = 0
+ value: int = 0 # -127 to 126
+
+
+@dataclass
+class SweepEvent:
+ type: str = "sweep"
+ tick_position: int = 0
+ speed: int = 0
+ depth: int = 0
+
+
+@dataclass
+class RelativeVolumeEvent:
+ type: str = "rel_volume"
+ tick_position: int = 0
+ delta: int = 0
+
+
+@dataclass
class EnvelopeEvent:
type: str = "envelope"
tick_position: int = 0
@@ -142,6 +171,61 @@ class EnvelopeEvent:
@dataclass
+class VolumeEnvelopeEvent:
+ type: str = "vol_envelope"
+ tick_position: int = 0
+ slot: int = 0
+ points: list[int] = field(default_factory=list)
+ loop_points: list[int] = field(default_factory=list)
+ is_definition: bool = False
+
+
+@dataclass
+class DutyEnvelopeEvent:
+ type: str = "duty_envelope"
+ tick_position: int = 0
+ slot: int = 0
+ points: list[int] = field(default_factory=list)
+ loop_points: list[int] = field(default_factory=list)
+ is_definition: bool = False
+
+
+@dataclass
+class LfoEvent:
+ type: str = "lfo"
+ tick_position: int = 0
+ slot: int = 0
+ delay: int = 0
+ speed: int = 0
+ depth: int = 0
+ transition: int = 0
+ is_definition: bool = False
+ is_off: bool = False
+
+
+@dataclass
+class PitchEnvEvent:
+ type: str = "pitch_envelope"
+ tick_position: int = 0
+ slot: int = 0
+ points: list[int] = field(default_factory=list)
+ loop_points: list[int] = field(default_factory=list)
+ is_definition: bool = False
+ is_off: bool = False
+
+
+@dataclass
+class NoteEnvEvent:
+ type: str = "note_envelope"
+ tick_position: int = 0
+ slot: int = 0
+ points: list[int] = field(default_factory=list)
+ loop_points: list[int] = field(default_factory=list)
+ is_definition: bool = False
+ is_off: bool = False
+
+
+@dataclass
class VibratoEvent:
type: str = "vibrato"
tick_position: int = 0
@@ -164,7 +248,16 @@ Event = (
| DutyEvent
| TempoEvent
| RepeatEvent
+ | QuantizeEvent
+ | DetuneEvent
+ | SweepEvent
+ | RelativeVolumeEvent
| EnvelopeEvent
+ | VolumeEnvelopeEvent
+ | DutyEnvelopeEvent
+ | LfoEvent
+ | PitchEnvEvent
+ | NoteEnvEvent
| VibratoEvent
| GlideEvent
)
@@ -209,9 +302,27 @@ class NoteSequence:
def event_to_dict(event: Event) -> dict[str, Any]:
if isinstance(
- event, (NoteEvent, RestEvent, VolumeEvent, DutyEvent, TempoEvent, RepeatEvent)
+ event,
+ (
+ NoteEvent,
+ RestEvent,
+ VolumeEvent,
+ DutyEvent,
+ TempoEvent,
+ RepeatEvent,
+ QuantizeEvent,
+ DetuneEvent,
+ SweepEvent,
+ RelativeVolumeEvent,
+ EnvelopeEvent,
+ VolumeEnvelopeEvent,
+ DutyEnvelopeEvent,
+ LfoEvent,
+ PitchEnvEvent,
+ NoteEnvEvent,
+ VibratoEvent,
+ GlideEvent,
+ ),
):
return event.__dict__
- if isinstance(event, (EnvelopeEvent, VibratoEvent, GlideEvent)):
- return event.__dict__
raise TypeError(f"Unsupported event type: {type(event)}")