Repository navigation
Description
当我尝试使用这个实例中的程序混合一个声音模型并保存到本地时,问题发生了.
import requests
response = requests.post(
"http://localhost:8880/v1/audio/voices/combine",
json="af_heart(2.2)+af_nicole(2.8)+af_jessica(1.5)",
headers={"Accept": "application/octet-stream"}
)
if response.status_code != 200:
print(f"Error: {response.text}")
exit(1)
with open("custom_voice.pt", "wb") as f:
f.write(response.content)
print("音色文件保存成功")
当json输入如上时,显然af_heart(2.2)会被下面这段相应的代码识别成一个模型,而非匹配权重.因此引发了错误:
Error: {"detail":{"error":"validation_error","message":"Base voice 'af_heart(2.2)' not found. Available voices:...]
@router.post("/audio/voices/combine")
async def combine_voices(request: Union[str, List[str]]):
"""Combine multiple voices into a new voice and return the .pt file.
Args:
request: Either a string with voices separated by + (e.g. "voice1+voice2")
or a list of voice names to combine
Returns:
FileResponse with the combined voice .pt file
Raises:
HTTPException:
- 400: Invalid request (wrong number of voices, voice not found)
- 500: Server error (file system issues, combination failed)
"""
# Check if local voice saving is allowed
if not settings.allow_local_voice_saving:
raise HTTPException(
status_code=403,
detail={
"error": "permission_denied",
"message": "Local voice saving is disabled",
"type": "permission_error",
},
)
try:
# Convert input to list of voices
if isinstance(request, str):
# Check if it's an OpenAI voice name
mapped_voice = _openai_mappings["voices"].get(request)
if mapped_voice:
request = mapped_voice
voices = [v.strip() for v in request.split("+") if v.strip()]
else:
# For list input, map each voice if it's an OpenAI voice name
voices = [_openai_mappings["voices"].get(v, v) for v in request]
voices = [v.strip() for v in voices if v.strip()]
if not voices:
raise ValueError("No voices provided")
# For multiple voices, validate base voices exist
tts_service = await get_tts_service()
available_voices = await tts_service.list_voices()
for voice in voices:
if voice not in available_voices:
raise ValueError(
f"Base voice '{voice}' not found. Available voices: {', '.join(sorted(available_voices))}"
)
# Combine voices
combined_tensor = await tts_service.combine_voices(voices=voices)
combined_name = "+".join(voices)
# Save to temp file
temp_dir = tempfile.gettempdir()
voice_path = os.path.join(temp_dir, f"{combined_name}.pt")
buffer = io.BytesIO()
torch.save(combined_tensor, buffer)
async with aiofiles.open(voice_path, "wb") as f:
await f.write(buffer.getvalue())
return FileResponse(
voice_path,
media_type="application/octet-stream",
filename=f"{combined_name}.pt",
headers={
"Content-Disposition": f"attachment; filename={combined_name}.pt",
"Cache-Control": "no-cache",
},
)
except ValueError as e:
logger.warning(f"Invalid voice combination request: {str(e)}")
raise HTTPException(
status_code=400,
detail={
"error": "validation_error",
"message": str(e),
"type": "invalid_request_error",
},
)
except RuntimeError as e:
logger.error(f"Voice combination processing error: {str(e)}")
raise HTTPException(
status_code=500,
detail={
"error": "processing_error",
"message": "Failed to process voice combination request",
"type": "server_error",
},
)
except Exception as e:
logger.error(f"Unexpected error in voice combination: {str(e)}")
raise HTTPException(
status_code=500,
detail={
"error": "server_error",
"message": "An unexpected error occurred",
"type": "server_error",
},
)
也许这个功能的实现代码以及README.md需要修改,如果我应该尝试别的方法,请回复我.
感谢这个项目,为我提供了很多便利!
Activity
BobSoft3000 commentedon Mar 18, 2026
The combine function doesn't appear to accept parentheses (unlike using the 'speech' API with a custom combination), So the example in the README.md doesn't currently work:
A workaround is to remove the parentheses and duplicate the models to get the same mix:
If its not viable to support specifying the model weightings directly, is it possible to update the documentation in the meantime?
Jakeshadow commentedon Jun 13, 2026
fix(voices): combine endpoint accepts weighted syntax, matching speec…
chore(release): 0.6.0 [skip ci]