""" VoiceVox API連携 VoiceVoxエンジンとの通信を担当 """ import os import json import hashlib from typing import Optional import urllib.request import urllib.error class VoiceVoxAPI: """VoiceVox エンジンとの通信を管理""" def __init__(self, host: str = "127.0.0.1", port: int = 50021): self.base_url = f"http://{host}:{port}" self.last_error = "" # 最後のエラーメッセージを保存 def test_connection(self) -> bool: """接続テスト""" try: url = f"{self.base_url}/version" with urllib.request.urlopen(url, timeout=5) as response: return response.status == 200 except Exception as e: print(f"Connection test failed: {e}") return False def get_speakers(self) -> Optional[list]: """利用可能な話者一覧を取得""" try: url = f"{self.base_url}/speakers" with urllib.request.urlopen(url, timeout=10) as response: data = response.read() return json.loads(data) except Exception as e: print(f"Failed to get speakers: {e}") return None def generate_speech( self, text: str, speaker_id: int = 0, speed_scale: float = 1.0, pitch_scale: float = 0.0, intonation_scale: float = 1.0, volume_scale: float = 1.0, output_dir: str = "/tmp", ) -> Optional[str]: """ 音声を生成して保存 Returns: 生成された音声ファイルのパス、失敗時はNone """ try: print(f"[VoiceVox API] Step 1: 出力ディレクトリを作成: {output_dir}") os.makedirs(output_dir, exist_ok=True) # 1. 音声合成用のクエリを作成 print(f"[VoiceVox API] Step 2: 音声クエリを生成中...") query_url = f"{self.base_url}/audio_query" query_params = urllib.parse.urlencode({ 'text': text, 'speaker': speaker_id, }) full_query_url = f"{query_url}?{query_params}" print(f"[VoiceVox API] リクエストURL: {full_query_url}") try: # POSTリクエストにするため空のdataを渡す req = urllib.request.Request(full_query_url, data=b'', method='POST') with urllib.request.urlopen(req, timeout=10) as response: query_data = json.loads(response.read()) print(f"[VoiceVox API] クエリ生成成功") except urllib.error.HTTPError as e: print(f"[VoiceVox API Error] HTTPエラー: {e.code} {e.reason}") print(f"[VoiceVox API Error] レスポンス: {e.read().decode('utf-8', errors='replace')}") raise except urllib.error.URLError as e: print(f"[VoiceVox API Error] 接続エラー: {e.reason}") print(f"[VoiceVox API Error] VoiceVoxエンジンが起動しているか確認してください") raise # パラメータを適用 query_data['speedScale'] = speed_scale query_data['pitchScale'] = pitch_scale query_data['intonationScale'] = intonation_scale query_data['volumeScale'] = volume_scale # 2. 音声合成を実行 print(f"[VoiceVox API] Step 3: 音声合成を実行中...") synthesis_url = f"{self.base_url}/synthesis" synthesis_params = urllib.parse.urlencode({'speaker': speaker_id}) req = urllib.request.Request( f"{synthesis_url}?{synthesis_params}", data=json.dumps(query_data).encode('utf-8'), headers={'Content-Type': 'application/json'}, method='POST', ) try: with urllib.request.urlopen(req, timeout=30) as response: audio_data = response.read() print(f"[VoiceVox API] 音声合成成功 ({len(audio_data)} bytes)") except urllib.error.HTTPError as e: print(f"[VoiceVox API Error] 合成HTTPエラー: {e.code} {e.reason}") print(f"[VoiceVox API Error] レスポンス: {e.read().decode('utf-8', errors='replace')}") raise # 3. 音声ファイルを保存 print(f"[VoiceVox API] Step 4: 音声ファイルを保存中...") text_hash = hashlib.md5(text.encode('utf-8')).hexdigest()[:8] filename = f"voicevox_{text_hash}_{speaker_id}.wav" filepath = os.path.join(output_dir, filename) with open(filepath, 'wb') as f: f.write(audio_data) print(f"[VoiceVox API] 音声保存完了: {filepath}") return filepath except urllib.error.URLError as e: error_msg = f"接続エラー: VoiceVoxエンジンに接続できません ({self.base_url})" print(f"[VoiceVox API Error] {error_msg}") print(f"[VoiceVox API Error] 詳細: {e}") self.last_error = error_msg import traceback traceback.print_exc() return None except urllib.error.HTTPError as e: error_msg = f"HTTPエラー {e.code}: {e.reason}" print(f"[VoiceVox API Error] {error_msg}") self.last_error = error_msg import traceback traceback.print_exc() return None except Exception as e: error_msg = f"{type(e).__name__}: {str(e)}" print(f"[VoiceVox API Error] 音声生成失敗: {error_msg}") self.last_error = error_msg import traceback traceback.print_exc() return None