Add french, italian, portuguese, spanish, german (#155)
The french 6 layers variant wasn't included because it's not good enough yet. Use the `french_24l` variant for now.
This commit is contained in:
+73
-25
@@ -22,12 +22,11 @@ from pocket_tts.default_parameters import (
|
||||
DEFAULT_LSD_DECODE_STEPS,
|
||||
DEFAULT_NOISE_CLAMP,
|
||||
DEFAULT_TEMPERATURE,
|
||||
DEFAULT_VARIANT,
|
||||
MAX_TOKEN_PER_CHUNK,
|
||||
)
|
||||
from pocket_tts.models.tts_model import TTSModel, export_model_state
|
||||
from pocket_tts.utils.logging_utils import enable_logging
|
||||
from pocket_tts.utils.utils import PREDEFINED_VOICES, size_of_dict
|
||||
from pocket_tts.utils.utils import _ORIGINS_OF_PREDEFINED_VOICES
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -42,7 +41,6 @@ cli_app = typer.Typer(
|
||||
|
||||
# Global model instance
|
||||
tts_model: TTSModel | None = None
|
||||
global_model_state = None
|
||||
|
||||
web_app = FastAPI(
|
||||
title="Kyutai Pocket TTS API", description="Text-to-Speech generation API", version="1.0.0"
|
||||
@@ -124,12 +122,15 @@ def text_to_speech(
|
||||
|
||||
Args:
|
||||
text: Text to convert to speech
|
||||
voice_url: Optional voice URL (http://, https://, or hf://)
|
||||
voice_url: Optional built-in voice name (e.g., "alba"), or voice URL (http://, https://, or hf://)
|
||||
voice_wav: Optional uploaded voice file (mutually exclusive with voice_url)
|
||||
"""
|
||||
if not text.strip():
|
||||
raise HTTPException(status_code=400, detail="Text cannot be empty")
|
||||
|
||||
if voice_url is None and voice_wav is None:
|
||||
voice_url = DEFAULT_AUDIO_PROMPT
|
||||
|
||||
if voice_url is not None and voice_wav is not None:
|
||||
raise HTTPException(status_code=400, detail="Cannot provide both voice_url and voice_wav")
|
||||
|
||||
@@ -139,7 +140,7 @@ def text_to_speech(
|
||||
voice_url.startswith("http://")
|
||||
or voice_url.startswith("https://")
|
||||
or voice_url.startswith("hf://")
|
||||
or voice_url in PREDEFINED_VOICES
|
||||
or voice_url in _ORIGINS_OF_PREDEFINED_VOICES
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=400, detail="voice_url must start with http://, https://, or hf://"
|
||||
@@ -161,8 +162,7 @@ def text_to_speech(
|
||||
finally:
|
||||
os.unlink(temp_file_path)
|
||||
else:
|
||||
# Use default global model state
|
||||
model_state = global_model_state
|
||||
raise HTTPException(status_code=500, detail="This should never happen.")
|
||||
|
||||
return StreamingResponse(
|
||||
generate_data_with_state(text, model_state),
|
||||
@@ -176,30 +176,33 @@ def text_to_speech(
|
||||
|
||||
@cli_app.command()
|
||||
def serve(
|
||||
voice: Annotated[
|
||||
str, typer.Option(help="Path to voice prompt audio file (voice to clone)")
|
||||
] = DEFAULT_AUDIO_PROMPT,
|
||||
host: Annotated[str, typer.Option(help="Host to bind to")] = "localhost",
|
||||
port: Annotated[int, typer.Option(help="Port to bind to")] = 8000,
|
||||
reload: Annotated[bool, typer.Option(help="Enable auto-reload")] = False,
|
||||
config: Annotated[
|
||||
str,
|
||||
language: Annotated[
|
||||
str | None,
|
||||
typer.Option(
|
||||
help="Path to locally-saved model config .yaml file or model variant signature"
|
||||
help="Language for the TTS model. "
|
||||
"'english_2026-01', 'english_2026-04', 'english', 'french_24l', 'german_24l', 'portuguese', 'italian', 'spanish'."
|
||||
" Incompatible with the config argument. Default is 'english', which is the same model as 'english_2026-04'.",
|
||||
show_default=False,
|
||||
),
|
||||
] = DEFAULT_VARIANT,
|
||||
] = None,
|
||||
config: Annotated[
|
||||
str | None,
|
||||
typer.Option(
|
||||
help="Path to locally-saved model config .yaml file. "
|
||||
"Incompatible with the language argument. If not provided, will use the default English model."
|
||||
),
|
||||
] = None,
|
||||
quantize: Annotated[
|
||||
bool, typer.Option(help="Apply int8 quantization to reduce memory usage")
|
||||
] = False,
|
||||
):
|
||||
"""Start the FastAPI server."""
|
||||
|
||||
global tts_model, global_model_state
|
||||
tts_model = TTSModel.load_model(config, quantize=quantize)
|
||||
|
||||
# Pre-load the voice prompt
|
||||
global_model_state = tts_model.get_state_for_audio_prompt(voice)
|
||||
logger.info(f"The size of the model state is {size_of_dict(global_model_state) // 1e6} MB")
|
||||
global tts_model
|
||||
tts_model = TTSModel.load_model(language=language, config=config, quantize=quantize)
|
||||
|
||||
uvicorn.run("pocket_tts.main:web_app", host=host, port=port, reload=reload)
|
||||
|
||||
@@ -218,9 +221,28 @@ def generate(
|
||||
str, typer.Option(help="Path to audio conditioning file (voice to clone)")
|
||||
] = DEFAULT_AUDIO_PROMPT,
|
||||
quiet: Annotated[bool, typer.Option("-q", "--quiet", help="Disable logging output")] = False,
|
||||
language: Annotated[
|
||||
str | None,
|
||||
typer.Option(
|
||||
help=(
|
||||
"Language for the TTS model. "
|
||||
"'english_2026-01', 'english_2026-04', 'english', 'french_24l', 'spanish_24l',"
|
||||
"'german_24l', 'portuguese_24l', 'italian_24l'."
|
||||
" Incompatible with the config argument. Default is 'english', which is the same model as 'english_2026-04'. "
|
||||
"The '24l' variants are bigger models, "
|
||||
"not distilled yet and here only as preview. They're not the final "
|
||||
"models for those languages."
|
||||
),
|
||||
show_default=False,
|
||||
),
|
||||
] = None,
|
||||
config: Annotated[
|
||||
str, typer.Option(help="Model signature or path to config .yaml file")
|
||||
] = DEFAULT_VARIANT,
|
||||
str | None,
|
||||
typer.Option(
|
||||
help="Path to locally-saved model config .yaml file. "
|
||||
"Incompatible with the language argument. If not provided, will use the default English model."
|
||||
),
|
||||
] = None,
|
||||
lsd_decode_steps: Annotated[
|
||||
int, typer.Option(help="Number of generation steps")
|
||||
] = DEFAULT_LSD_DECODE_STEPS,
|
||||
@@ -254,7 +276,13 @@ def generate(
|
||||
logger.error("No input received from stdin.")
|
||||
raise typer.Exit(code=1)
|
||||
tts_model = TTSModel.load_model(
|
||||
config, temperature, lsd_decode_steps, noise_clamp, eos_threshold, quantize=quantize
|
||||
language=language,
|
||||
config=config,
|
||||
temp=temperature,
|
||||
lsd_decode_steps=lsd_decode_steps,
|
||||
noise_clamp=noise_clamp,
|
||||
eos_threshold=eos_threshold,
|
||||
quantize=quantize,
|
||||
)
|
||||
tts_model.to(device)
|
||||
|
||||
@@ -293,13 +321,33 @@ def export_voice(
|
||||
],
|
||||
export_path: Annotated[str, typer.Argument(help="Output file or directory")],
|
||||
quiet: Annotated[bool, typer.Option("-q", "--quiet", help="Disable logging output")] = False,
|
||||
config: Annotated[str, typer.Option(help="Model config path or signature")] = DEFAULT_VARIANT,
|
||||
language: Annotated[
|
||||
str | None,
|
||||
typer.Option(
|
||||
help=(
|
||||
"Language for the TTS model. "
|
||||
"'english_2026-01', 'english_2026-04', 'english', 'french_24l', 'german_24l','spanish_24l',"
|
||||
" 'portuguese_24l', 'italian_24l'."
|
||||
" Incompatible with the config argument. Default is 'english', which is the same model as 'english_2026-04'. "
|
||||
"The '24l' variants are bigger models, "
|
||||
"not distilled yet and here only as preview."
|
||||
),
|
||||
show_default=False,
|
||||
),
|
||||
] = None,
|
||||
config: Annotated[
|
||||
str | None,
|
||||
typer.Option(
|
||||
help="Path to locally-saved model config .yaml file. "
|
||||
"Incompatible with the language argument. If not provided, will use the default English model."
|
||||
),
|
||||
] = None,
|
||||
):
|
||||
"""Convert and save audio to .safetensors file"""
|
||||
|
||||
log_level = logging.ERROR if quiet else logging.INFO
|
||||
with enable_logging("pocket_tts", log_level):
|
||||
tts_model = TTSModel.load_model(config)
|
||||
tts_model = TTSModel.load_model(language=language, config=config)
|
||||
model_state = tts_model.get_state_for_audio_prompt(
|
||||
audio_conditioning=audio_path, truncate=True
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user