summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-07-12 18:50:50 +0900
committertanaka takashi <isazaka@gmail.com>2026-07-12 18:50:50 +0900
commitd83a966199671a1cab6b8f56be448662e79f4a46 (patch)
tree88f1ee34230e5f60411bf988a251b00c3c9fe8ad /tests
parent3d06bee12a26bc55cd73b1494e4d008a8098f6f5 (diff)
feat: 合成機能テンプレートの拡充とドキュメントの更新
- ppmck/pyxel向けに表現力のある作曲テンプレートを追加 - エンベロープ、ビブラート、音程変化の使用例を追加 - テンプレートの検証・IR・音声合成テストを追加 - README、設計書、MCP仕様書を現行実装に合わせて更新
Diffstat (limited to 'tests')
-rw-r--r--tests/test_compose_mml_tool.py23
-rw-r--r--tests/test_templates.py51
2 files changed, 70 insertions, 4 deletions
diff --git a/tests/test_compose_mml_tool.py b/tests/test_compose_mml_tool.py
index 3d56115..be4735e 100644
--- a/tests/test_compose_mml_tool.py
+++ b/tests/test_compose_mml_tool.py
@@ -65,7 +65,16 @@ def test_validate_pyxel(compose_mml):
def test_template_all(compose_mml):
for mode in ("ppmck", "pyxel"):
- for template in ("basic", "melody", "chord", "drum", "empty"):
+ for template in (
+ "basic",
+ "melody",
+ "chord",
+ "drum",
+ "empty",
+ "expressive_lead",
+ "vibrato_lead",
+ "pitch_motion",
+ ):
result = compose_mml(action="template", mode=mode, template=template)
assert "mml" in result
assert "description" in result
@@ -77,6 +86,18 @@ def test_template_all(compose_mml):
)
+def test_synthesis_templates_compose(compose_mml, tmp_output_dir):
+ for mode in ("ppmck", "pyxel"):
+ for template in ("expressive_lead", "vibrato_lead", "pitch_motion"):
+ generated = compose_mml(action="template", mode=mode, template=template)
+ result = compose_mml(action="compose", mml=generated["mml"], mode=mode)
+ assert result["success"] is True, (
+ f"{mode}/{template} synthesis failed: {result['validation']}"
+ )
+ assert result["duration_sec"] > 0
+ assert Path(result["wav_path"]).exists()
+
+
def test_template_invalid_fallback(compose_mml):
result = compose_mml(action="template", mode="pyxel", template="unknown")
assert result["description"] == "基本的な4ch構成(メロディ+和音+ベース+リズム)"
diff --git a/tests/test_templates.py b/tests/test_templates.py
index c6837b0..5fcf893 100644
--- a/tests/test_templates.py
+++ b/tests/test_templates.py
@@ -5,17 +5,28 @@ import pytest
from mml_composemusic_mcp.server import compose_mml
from mml_composemusic_mcp.templates import DESCRIPTIONS, TEMPLATES, get_template
+TEMPLATE_NAMES = (
+ "basic",
+ "melody",
+ "chord",
+ "drum",
+ "empty",
+ "expressive_lead",
+ "vibrato_lead",
+ "pitch_motion",
+)
+
def test_all_templates_exist():
for mode in ("ppmck", "pyxel"):
- for name in ("basic", "melody", "chord", "drum", "empty"):
+ for name in TEMPLATE_NAMES:
assert mode in TEMPLATES
assert name in TEMPLATES[mode]
assert TEMPLATES[mode][name]
def test_all_descriptions_exist():
- for name in ("basic", "melody", "chord", "drum", "empty"):
+ for name in TEMPLATE_NAMES:
assert name in DESCRIPTIONS
assert DESCRIPTIONS[name]
@@ -51,7 +62,7 @@ def test_get_template_empty():
@pytest.mark.parametrize("mode", ["ppmck", "pyxel"])
-@pytest.mark.parametrize("template", ["basic", "melody", "chord", "drum", "empty"])
+@pytest.mark.parametrize("template", TEMPLATE_NAMES)
def test_template_validates(mode, template):
mml, _ = get_template(mode, template)
result = compose_mml(action="validate", mml=mml, mode=mode)
@@ -81,3 +92,37 @@ def test_template_invalid_name_falls_back():
result = compose_mml(action="template", mode="ppmck", template="nonexistent")
assert result["mml"]
assert result["description"]
+
+
+@pytest.mark.parametrize(
+ ("mode", "template", "event_types"),
+ [
+ ("ppmck", "expressive_lead", {"vol_envelope", "duty_envelope", "rel_volume"}),
+ ("pyxel", "expressive_lead", {"envelope"}),
+ ("ppmck", "vibrato_lead", {"lfo", "pitch_envelope", "detune"}),
+ ("pyxel", "vibrato_lead", {"vibrato"}),
+ ("ppmck", "pitch_motion", {"note_envelope", "sweep"}),
+ ("pyxel", "pitch_motion", {"glide"}),
+ ],
+)
+def test_synthesis_templates_contain_expected_events(mode, template, event_types):
+ mml, _ = get_template(mode, template)
+ result = compose_mml(action="validate", mml=mml, mode=mode)
+ actual_types = {
+ event["type"]
+ for channel in result["note_sequence"]["channels"].values()
+ for event in channel["events"]
+ }
+ assert event_types <= actual_types
+
+
+def test_pyxel_vibrato_template_applies_detune_to_notes():
+ mml, _ = get_template("pyxel", "vibrato_lead")
+ result = compose_mml(action="validate", mml=mml, mode="pyxel")
+ notes = [
+ event
+ for channel in result["note_sequence"]["channels"].values()
+ for event in channel["events"]
+ if event["type"] == "note"
+ ]
+ assert any(note["detune_cents"] != 0 for note in notes)