Jacky2305 commited on
Commit
68adfb3
·
1 Parent(s): d8fe1cd

Fix Piper 1.2+ synthesize API: handle single-speaker models (no sid input)

Browse files
Files changed (1) hide show
  1. main.py +36 -8
main.py CHANGED
@@ -12,23 +12,28 @@ from pathlib import Path
12
 
13
  app = FastAPI(title="Piper TTS Space")
14
 
15
- # Voice model info: (lang, model_name, url_onnx, url_json)
16
  VOICE_MODELS = {
17
  "zh": ("zh_CN-huayan-medium",
18
  "https://huggingface.co/rhasspy/piper-voices/resolve/main/zh/zh_CN/huayan/medium/zh_CN-huayan-medium.onnx",
19
- "https://huggingface.co/rhasspy/piper-voices/resolve/main/zh/zh_CN/huayan/medium/zh_CN-huayan-medium.onnx.json"),
 
20
  "en": ("en_US-lessac-medium",
21
  "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/medium/en_US-lessac-medium.onnx",
22
- "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/medium/en_US-lessac-medium.onnx.json"),
 
23
  "ms": ("ms_MY-sirius-medium",
24
  "https://huggingface.co/rhasspy/piper-voices/resolve/main/ms/ms_MY/sirius/medium/ms_MY-sirius-medium.onnx",
25
- "https://huggingface.co/rhasspy/piper-voices/resolve/main/ms/ms_MY/sirius/medium/ms_MY-sirius-medium.onnx.json"),
 
26
  "ja": ("ja_JP-kenji-medium",
27
  "https://huggingface.co/rhasspy/piper-voices/resolve/main/ja/ja_JP/kenji/medium/ja_JP-kenji-medium.onnx",
28
- "https://huggingface.co/rhasspy/piper-voices/resolve/main/ja/ja_JP/kenji/medium/ja_JP-kenji-medium.onnx.json"),
 
29
  "ko": ("ko_KR-hui-hao-medium",
30
  "https://huggingface.co/rhasspy/piper-voices/resolve/main/ko/ko_KR/hui_hao/medium/ko_KR-hui-hao-medium.onnx",
31
- "https://huggingface.co/rhasspy/piper-voices/resolve/main/ko/ko_KR/hui_hao/medium/ko_KR-hui-hao-medium.onnx.json"),
 
32
  }
33
 
34
  MODEL_DIR = Path("/tmp/piper_models")
@@ -51,7 +56,7 @@ def get_voice(lang: str) -> PiperVoice:
51
  if lang not in VOICE_MODELS:
52
  raise ValueError(f"Language '{lang}' not available. Available: {list(VOICE_MODELS.keys())}")
53
 
54
- model_name, url_onnx, url_json = VOICE_MODELS[lang]
55
  onnx_path = MODEL_DIR / f"{model_name}.onnx"
56
  json_path = MODEL_DIR / f"{model_name}.onnx.json"
57
 
@@ -91,13 +96,19 @@ async def tts(request: TTSRequest):
91
  raise HTTPException(400, f"Voice load failed: {str(e)}")
92
 
93
  try:
 
 
 
94
  # Piper 1.2+ requires wav_file parameter - use wave.open with BytesIO
95
  buffer = BytesIO()
96
  with wave.open(buffer, 'wb') as wav_file:
97
  wav_file.setnchannels(1)
98
  wav_file.setsampwidth(2) # 16-bit
99
  wav_file.setframerate(request.sample_rate)
100
- voice.synthesize(request.text, speaker_id=request.speaker_id, wav_file=wav_file)
 
 
 
101
  buffer.seek(0)
102
  return Response(
103
  content=buffer.read(),
@@ -105,6 +116,23 @@ async def tts(request: TTSRequest):
105
  headers={"Content-Disposition": f"attachment; filename=tts_{uuid.uuid4().hex}.wav"}
106
  )
107
  except Exception as e:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  raise HTTPException(500, f"Synthesis failed: {str(e)}")
109
 
110
  @app.get("/health")
 
12
 
13
  app = FastAPI(title="Piper TTS Space")
14
 
15
+ # Voice model info: (lang, model_name, url_onnx, url_json, is_multi_speaker)
16
  VOICE_MODELS = {
17
  "zh": ("zh_CN-huayan-medium",
18
  "https://huggingface.co/rhasspy/piper-voices/resolve/main/zh/zh_CN/huayan/medium/zh_CN-huayan-medium.onnx",
19
+ "https://huggingface.co/rhasspy/piper-voices/resolve/main/zh/zh_CN/huayan/medium/zh_CN-huayan-medium.onnx.json",
20
+ False),
21
  "en": ("en_US-lessac-medium",
22
  "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/medium/en_US-lessac-medium.onnx",
23
+ "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/medium/en_US-lessac-medium.onnx.json",
24
+ False),
25
  "ms": ("ms_MY-sirius-medium",
26
  "https://huggingface.co/rhasspy/piper-voices/resolve/main/ms/ms_MY/sirius/medium/ms_MY-sirius-medium.onnx",
27
+ "https://huggingface.co/rhasspy/piper-voices/resolve/main/ms/ms_MY/sirius/medium/ms_MY-sirius-medium.onnx.json",
28
+ False),
29
  "ja": ("ja_JP-kenji-medium",
30
  "https://huggingface.co/rhasspy/piper-voices/resolve/main/ja/ja_JP/kenji/medium/ja_JP-kenji-medium.onnx",
31
+ "https://huggingface.co/rhasspy/piper-voices/resolve/main/ja/ja_JP/kenji/medium/ja_JP-kenji-medium.onnx.json",
32
+ False),
33
  "ko": ("ko_KR-hui-hao-medium",
34
  "https://huggingface.co/rhasspy/piper-voices/resolve/main/ko/ko_KR/hui_hao/medium/ko_KR-hui-hao-medium.onnx",
35
+ "https://huggingface.co/rhasspy/piper-voices/resolve/main/ko/ko_KR/hui_hao/medium/ko_KR-hui-hao-medium.onnx.json",
36
+ False),
37
  }
38
 
39
  MODEL_DIR = Path("/tmp/piper_models")
 
56
  if lang not in VOICE_MODELS:
57
  raise ValueError(f"Language '{lang}' not available. Available: {list(VOICE_MODELS.keys())}")
58
 
59
+ model_name, url_onnx, url_json, _ = VOICE_MODELS[lang]
60
  onnx_path = MODEL_DIR / f"{model_name}.onnx"
61
  json_path = MODEL_DIR / f"{model_name}.onnx.json"
62
 
 
96
  raise HTTPException(400, f"Voice load failed: {str(e)}")
97
 
98
  try:
99
+ # Check if model supports multi-speaker
100
+ _, _, _, is_multi_speaker = VOICE_MODELS[request.lang]
101
+
102
  # Piper 1.2+ requires wav_file parameter - use wave.open with BytesIO
103
  buffer = BytesIO()
104
  with wave.open(buffer, 'wb') as wav_file:
105
  wav_file.setnchannels(1)
106
  wav_file.setsampwidth(2) # 16-bit
107
  wav_file.setframerate(request.sample_rate)
108
+ if is_multi_speaker:
109
+ voice.synthesize(request.text, speaker_id=request.speaker_id, wav_file=wav_file)
110
+ else:
111
+ voice.synthesize(request.text, wav_file=wav_file)
112
  buffer.seek(0)
113
  return Response(
114
  content=buffer.read(),
 
116
  headers={"Content-Disposition": f"attachment; filename=tts_{uuid.uuid4().hex}.wav"}
117
  )
118
  except Exception as e:
119
+ # Fallback: try without speaker_id if it fails
120
+ if "sid" in str(e) or "speaker" in str(e).lower():
121
+ try:
122
+ buffer = BytesIO()
123
+ with wave.open(buffer, 'wb') as wav_file:
124
+ wav_file.setnchannels(1)
125
+ wav_file.setsampwidth(2)
126
+ wav_file.setframerate(request.sample_rate)
127
+ voice.synthesize(request.text, wav_file=wav_file)
128
+ buffer.seek(0)
129
+ return Response(
130
+ content=buffer.read(),
131
+ media_type="audio/wav",
132
+ headers={"Content-Disposition": f"attachment; filename=tts_{uuid.uuid4().hex}.wav"}
133
+ )
134
+ except Exception as e2:
135
+ raise HTTPException(500, f"Synthesis failed (fallback): {str(e2)}")
136
  raise HTTPException(500, f"Synthesis failed: {str(e)}")
137
 
138
  @app.get("/health")