Feat/add chunk size control arg (#56)

This pull request addresses issue #38 

**Changes included:**

- Added a `max_tokens` parameter to the typer cli for the `generate`
command, allowing user to set a default number of tokens per chunk.
- Introduced a default variable `MAX_TOKEN_PER_CHUNK` to centralize the
default value.
- Fixed a tokenization issue by inserting a comma, allowing the model to
correctly read each chunk as intended rather than treating one chunk as
a single sentence.

**Future improvement suggestion :**

- Consider enhancing the `split_into_best_sentences` function using
regex to split text more accurately without modifying it, which would
improve clarity and maintainability.

---------

Co-authored-by: Václav Volhejn <8401624+vvolhejn@users.noreply.github.com>
This commit is contained in:
Saleem Djima
2026-01-21 11:54:53 +01:00
committed by GitHub
co-authored by Václav Volhejn
parent 36f21a0a7f
commit 4ec89acf95
4 changed files with 16 additions and 4 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
"""Various utilities for audio convertion (pcm format, sample rate and channels), """Various utilities for audio conversion (pcm format, sample rate and channels),
and volume normalization.""" and volume normalization."""
import torch import torch
+1
View File
@@ -5,3 +5,4 @@ DEFAULT_LSD_DECODE_STEPS = 1
DEFAULT_NOISE_CLAMP = None DEFAULT_NOISE_CLAMP = None
DEFAULT_EOS_THRESHOLD = -4.0 DEFAULT_EOS_THRESHOLD = -4.0
DEFAULT_FRAMES_AFTER_EOS = None DEFAULT_FRAMES_AFTER_EOS = None
MAX_TOKEN_PER_CHUNK = 50
+5
View File
@@ -22,6 +22,7 @@ from pocket_tts.default_parameters import (
DEFAULT_NOISE_CLAMP, DEFAULT_NOISE_CLAMP,
DEFAULT_TEMPERATURE, DEFAULT_TEMPERATURE,
DEFAULT_VARIANT, DEFAULT_VARIANT,
MAX_TOKEN_PER_CHUNK,
) )
from pocket_tts.models.tts_model import TTSModel from pocket_tts.models.tts_model import TTSModel
from pocket_tts.utils.logging_utils import enable_logging from pocket_tts.utils.logging_utils import enable_logging
@@ -223,6 +224,9 @@ def generate(
str, typer.Option(help="Output path for generated audio") str, typer.Option(help="Output path for generated audio")
] = "./tts_output.wav", ] = "./tts_output.wav",
device: Annotated[str, typer.Option(help="Device to use")] = "cpu", device: Annotated[str, typer.Option(help="Device to use")] = "cpu",
max_tokens: Annotated[
int, typer.Option(help="Maximum number of tokens per chunk.")
] = MAX_TOKEN_PER_CHUNK,
): ):
"""Generate speech using Kyutai Pocket TTS.""" """Generate speech using Kyutai Pocket TTS."""
if "cuda" in device: if "cuda" in device:
@@ -242,6 +246,7 @@ def generate(
model_state=model_state_for_voice, model_state=model_state_for_voice,
text_to_generate=text, text_to_generate=text,
frames_after_eos=frames_after_eos, frames_after_eos=frames_after_eos,
max_tokens=max_tokens,
) )
stream_audio_chunks(output_path, audio_chunks, tts_model.config.mimi.sample_rate) stream_audio_chunks(output_path, audio_chunks, tts_model.config.mimi.sample_rate)
+9 -3
View File
@@ -23,6 +23,7 @@ from pocket_tts.default_parameters import (
DEFAULT_NOISE_CLAMP, DEFAULT_NOISE_CLAMP,
DEFAULT_TEMPERATURE, DEFAULT_TEMPERATURE,
DEFAULT_VARIANT, DEFAULT_VARIANT,
MAX_TOKEN_PER_CHUNK,
) )
from pocket_tts.models.flow_lm import FlowLMModel from pocket_tts.models.flow_lm import FlowLMModel
from pocket_tts.models.mimi import MimiModel from pocket_tts.models.mimi import MimiModel
@@ -363,6 +364,7 @@ class TTSModel(nn.Module):
self, self,
model_state: dict, model_state: dict,
text_to_generate: str, text_to_generate: str,
max_tokens: int = MAX_TOKEN_PER_CHUNK,
frames_after_eos: int | None = None, frames_after_eos: int | None = None,
copy_state: bool = True, copy_state: bool = True,
) -> torch.Tensor: ) -> torch.Tensor:
@@ -405,6 +407,7 @@ class TTSModel(nn.Module):
text_to_generate=text_to_generate, text_to_generate=text_to_generate,
frames_after_eos=frames_after_eos, frames_after_eos=frames_after_eos,
copy_state=copy_state, copy_state=copy_state,
max_tokens=max_tokens,
): ):
audio_chunks.append(chunk) audio_chunks.append(chunk)
return torch.cat(audio_chunks, dim=0) return torch.cat(audio_chunks, dim=0)
@@ -414,6 +417,7 @@ class TTSModel(nn.Module):
self, self,
model_state: dict, model_state: dict,
text_to_generate: str, text_to_generate: str,
max_tokens: int = MAX_TOKEN_PER_CHUNK,
frames_after_eos: int | None = None, frames_after_eos: int | None = None,
copy_state: bool = True, copy_state: bool = True,
): ):
@@ -458,7 +462,9 @@ class TTSModel(nn.Module):
# by using teacher forcing, but it would be a bit slower. # by using teacher forcing, but it would be a bit slower.
# TODO: add the teacher forcing method for long texts where we use the audio of one chunk # TODO: add the teacher forcing method for long texts where we use the audio of one chunk
# as conditioning for the next chunk. # as conditioning for the next chunk.
chunks = split_into_best_sentences(self.flow_lm.conditioner.tokenizer, text_to_generate) chunks = split_into_best_sentences(
self.flow_lm.conditioner.tokenizer, text_to_generate, max_tokens
)
for chunk in chunks: for chunk in chunks:
text_to_generate, frames_after_eos_guess = prepare_text_prompt(chunk) text_to_generate, frames_after_eos_guess = prepare_text_prompt(chunk)
@@ -733,7 +739,7 @@ def prepare_text_prompt(text: str) -> tuple[str, int]:
return text, frames_after_eos_guess return text, frames_after_eos_guess
def split_into_best_sentences(tokenizer, text_to_generate: str) -> list[str]: def split_into_best_sentences(tokenizer, text_to_generate: str, max_tokens: int) -> list[str]:
text_to_generate, _ = prepare_text_prompt(text_to_generate) text_to_generate, _ = prepare_text_prompt(text_to_generate)
text_to_generate = text_to_generate.strip() text_to_generate = text_to_generate.strip()
tokens = tokenizer(text_to_generate) tokens = tokenizer(text_to_generate)
@@ -761,7 +767,7 @@ def split_into_best_sentences(tokenizer, text_to_generate: str) -> list[str]:
text = tokenizer.sp.decode(list_of_tokens[start:end]) text = tokenizer.sp.decode(list_of_tokens[start:end])
nb_tokens_and_sentences.append((end - start, text)) nb_tokens_and_sentences.append((end - start, text))
max_nb_tokens_in_a_chunk = 50 max_nb_tokens_in_a_chunk = max_tokens
chunks = [] chunks = []
current_chunk = "" current_chunk = ""
current_nb_of_tokens_in_chunk = 0 current_nb_of_tokens_in_chunk = 0