summaryrefslogtreecommitdiff
path: root/src/mml_composemusic_mcp
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-07-12 22:28:14 +0900
committertanaka takashi <isazaka@gmail.com>2026-07-12 22:28:14 +0900
commit764a892b43ba55e3d96b0d8986c64cd6b5d42236 (patch)
treed3ea5c0c3bc491f4de220059db3d36acd98153fe /src/mml_composemusic_mcp
parentd83a966199671a1cab6b8f56be448662e79f4a46 (diff)
waveファイルの出力先変更
mmlファイルをwaveファイルと一緒に出力するように変更 サンプルデータ追加
Diffstat (limited to 'src/mml_composemusic_mcp')
-rw-r--r--src/mml_composemusic_mcp/ir.py1
-rw-r--r--src/mml_composemusic_mcp/server.py43
2 files changed, 34 insertions, 10 deletions
diff --git a/src/mml_composemusic_mcp/ir.py b/src/mml_composemusic_mcp/ir.py
index d9a2f93..8e9b0d0 100644
--- a/src/mml_composemusic_mcp/ir.py
+++ b/src/mml_composemusic_mcp/ir.py
@@ -50,6 +50,7 @@ class ErrorCode(Enum):
# --- Runtime errors (Synthesizer phase) ---
RUNTIME_SYNTHESIS_FAILED = "RUNTIME_SYNTHESIS_FAILED"
RUNTIME_WAV_WRITE_FAILED = "RUNTIME_WAV_WRITE_FAILED"
+ RUNTIME_MML_WRITE_FAILED = "RUNTIME_MML_WRITE_FAILED"
RUNTIME_INTERNAL_ERROR = "RUNTIME_INTERNAL_ERROR"
# --- API-level errors (parameter validation, not MML syntax) ---
diff --git a/src/mml_composemusic_mcp/server.py b/src/mml_composemusic_mcp/server.py
index 45bae68..e3612da 100644
--- a/src/mml_composemusic_mcp/server.py
+++ b/src/mml_composemusic_mcp/server.py
@@ -1,7 +1,7 @@
# MCP server exposing compose_mml tool.
import argparse
-from datetime import datetime
+from datetime import datetime, timedelta
from pathlib import Path
from fastmcp import FastMCP
@@ -23,17 +23,24 @@ def _is_error(errors: list[ErrorDetail]) -> bool:
return any(e.severity == "error" for e in errors)
-def _generate_wav_filename() -> str:
- """Generate a unique WAV filename based on the current timestamp.
+def _create_output_directory() -> Path:
+ """Create and return a unique timestamped output directory.
- Format: output_YYYYMMDD_HHMMSS_mmm.wav
- Milliseconds are included to reduce the chance of collisions when
- multiple compose requests are issued in rapid succession.
+ Format: YYYYMMDD_HHMMSS_mmm
+ If the millisecond is already in use, advance the timestamp until a free
+ directory can be created. Exclusive creation also handles concurrent calls.
"""
now = datetime.now()
- # %f gives microseconds (6 digits); keep the first 3 for milliseconds.
- timestamp = now.strftime("%Y%m%d_%H%M%S_") + now.strftime("%f")[:3]
- return f"output_{timestamp}.wav"
+ for offset_ms in range(1000):
+ candidate_time = now + timedelta(milliseconds=offset_ms)
+ timestamp = candidate_time.strftime("%Y%m%d_%H%M%S_%f")[:-3]
+ output_dir = OUTPUT_DIR / timestamp
+ try:
+ output_dir.mkdir(parents=True, exist_ok=False)
+ return output_dir
+ except FileExistsError:
+ continue
+ raise RuntimeError("一意な出力ディレクトリを作成できませんでした。")
def _split_errors(errors: list[ErrorDetail]) -> tuple[list[dict], list[dict]]:
@@ -173,9 +180,25 @@ def compose_mml(
"note_sequence": note_sequence_dict,
"validation": {"errors": errors_list, "warnings": warnings_list},
}
- wav_path = OUTPUT_DIR / _generate_wav_filename()
+ output_dir = _create_output_directory()
+ wav_path = output_dir / "output.wav"
wav_errors = write_wav(wav_path, wave_data, sample_rate)
errors.extend(wav_errors)
+ if not _is_error(errors):
+ try:
+ (output_dir / "output.mml").write_text(mml, encoding="utf-8")
+ except Exception as exc:
+ errors.append(
+ ErrorDetail(
+ code=ErrorCode.RUNTIME_MML_WRITE_FAILED,
+ phase=ErrorPhase.RUNTIME,
+ line=0,
+ column=0,
+ message=f"MMLファイルの出力に失敗しました: {exc}",
+ severity="error",
+ hint="出力先を確認してください。",
+ )
+ )
errors_list, warnings_list = _split_errors(errors)
return {
"success": not _is_error(errors),