diff options
| author | tanaka takashi <isazaka@gmail.com> | 2026-07-12 18:50:50 +0900 |
|---|---|---|
| committer | tanaka takashi <isazaka@gmail.com> | 2026-07-12 18:50:50 +0900 |
| commit | d83a966199671a1cab6b8f56be448662e79f4a46 (patch) | |
| tree | 88f1ee34230e5f60411bf988a251b00c3c9fe8ad /tests | |
| parent | 3d06bee12a26bc55cd73b1494e4d008a8098f6f5 (diff) | |
feat: 合成機能テンプレートの拡充とドキュメントの更新
- ppmck/pyxel向けに表現力のある作曲テンプレートを追加
- エンベロープ、ビブラート、音程変化の使用例を追加
- テンプレートの検証・IR・音声合成テストを追加
- README、設計書、MCP仕様書を現行実装に合わせて更新
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_compose_mml_tool.py | 23 | ||||
| -rw-r--r-- | tests/test_templates.py | 51 |
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) |
