diff options
| author | tanaka takashi <isazaka@gmail.com> | 2026-07-12 22:28:14 +0900 |
|---|---|---|
| committer | tanaka takashi <isazaka@gmail.com> | 2026-07-12 22:28:14 +0900 |
| commit | 764a892b43ba55e3d96b0d8986c64cd6b5d42236 (patch) | |
| tree | d3ea5c0c3bc491f4de220059db3d36acd98153fe /src | |
| parent | d83a966199671a1cab6b8f56be448662e79f4a46 (diff) | |
waveファイルの出力先変更
mmlファイルをwaveファイルと一緒に出力するように変更
サンプルデータ追加
Diffstat (limited to 'src')
| -rw-r--r-- | src/mml_composemusic_mcp/ir.py | 1 | ||||
| -rw-r--r-- | src/mml_composemusic_mcp/server.py | 43 |
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), |
