Skip to content

supertonic.utils

supertonic.utils

Utility functions for Supertonic TTS.

This module provides various helper functions for text processing, file operations, and timing operations used throughout the Supertonic TTS package.

Functions:

Name Description
sanitize_filename

Sanitize filename by replacing non-alphanumeric characters.

timer

Context manager for timing code execution.

format_duration

Format duration in seconds to human-readable string.

get_audio_duration

Calculate audio duration from waveform length.

ensure_dir

Ensure directory exists, create if necessary.

validate_voice_style_format

Validate voice style JSON format.

chunk_text

Split text into chunks by paragraphs and sentences.

Attributes:

Name Type Description
logger

logger module-attribute

logger = getLogger(__name__)

sanitize_filename

sanitize_filename(text: str, max_len: int = 50) -> str

Sanitize filename by replacing non-alphanumeric characters.

Parameters:

Name Type Description Default
text str

Input text to convert to filename

required
max_len int

Maximum length of filename

50

Returns:

Type Description
str

Sanitized filename string

Source code in supertonic/utils.py
def sanitize_filename(text: str, max_len: int = 50) -> str:
    """
    Sanitize filename by replacing non-alphanumeric characters.

    Args:
        text: Input text to convert to filename
        max_len: Maximum length of filename

    Returns:
        Sanitized filename string
    """
    prefix = text[:max_len]
    return re.sub(r"[^a-zA-Z0-9_-]", "_", prefix)

timer

timer(name: str, verbose: bool = True)

Context manager for timing code execution.

Parameters:

Name Type Description Default
name str

Name of the operation being timed

required
verbose bool

Whether to log timing information

True
Example
with timer("Processing"):
    # Your code here
    process_data()
Source code in supertonic/utils.py
@contextmanager
def timer(name: str, verbose: bool = True):
    """
    Context manager for timing code execution.

    Args:
        name: Name of the operation being timed
        verbose: Whether to log timing information

    Example:
        ```python
        with timer("Processing"):
            # Your code here
            process_data()
        ```
    """
    if verbose:
        logger.info(f"{name}...")
    start = time.time()
    yield
    elapsed = time.time() - start
    if verbose:
        logger.info(f"{name} completed in {elapsed:.2f}s")

format_duration

format_duration(seconds: float) -> str

Format duration in seconds to human-readable string.

Parameters:

Name Type Description Default
seconds float

Duration in seconds

required

Returns:

Type Description
str

Formatted string (e.g., "1.23s", "2m 30s")

Source code in supertonic/utils.py
def format_duration(seconds: float) -> str:
    """
    Format duration in seconds to human-readable string.

    Args:
        seconds: Duration in seconds

    Returns:
        Formatted string (e.g., "1.23s", "2m 30s")
    """
    if seconds < 60:
        return f"{seconds:.2f}s"
    elif seconds < 3600:
        minutes = int(seconds // 60)
        secs = seconds % 60
        return f"{minutes}m {secs:.0f}s"
    else:
        hours = int(seconds // 3600)
        minutes = int((seconds % 3600) // 60)
        return f"{hours}h {minutes}m"

get_audio_duration

get_audio_duration(
    wav_length: int, sample_rate: int
) -> float

Calculate audio duration from waveform length.

Parameters:

Name Type Description Default
wav_length int

Number of samples in waveform

required
sample_rate int

Audio sample rate (Hz)

required

Returns:

Type Description
float

Duration in seconds

Source code in supertonic/utils.py
def get_audio_duration(wav_length: int, sample_rate: int) -> float:
    """
    Calculate audio duration from waveform length.

    Args:
        wav_length: Number of samples in waveform
        sample_rate: Audio sample rate (Hz)

    Returns:
        Duration in seconds
    """
    return wav_length / sample_rate

ensure_dir

ensure_dir(path: str) -> str

Ensure directory exists, create if necessary.

Parameters:

Name Type Description Default
path str

Directory path

required

Returns:

Type Description
str

Absolute path to directory

Source code in supertonic/utils.py
def ensure_dir(path: str) -> str:
    """
    Ensure directory exists, create if necessary.

    Args:
        path: Directory path

    Returns:
        Absolute path to directory
    """
    os.makedirs(path, exist_ok=True)
    return os.path.abspath(path)

validate_voice_style_format

validate_voice_style_format(style_data: dict) -> bool

Validate voice style JSON format.

Parameters:

Name Type Description Default
style_data dict

Voice style dictionary

required

Returns:

Type Description
bool

True if valid, False otherwise

Source code in supertonic/utils.py
def validate_voice_style_format(style_data: dict) -> bool:
    """
    Validate voice style JSON format.

    Args:
        style_data: Voice style dictionary

    Returns:
        True if valid, False otherwise
    """
    required_keys = ["style_ttl", "style_dp"]
    if not all(key in style_data for key in required_keys):
        return False

    for key in required_keys:
        if "dims" not in style_data[key] or "data" not in style_data[key]:
            return False

    return True

chunk_text

chunk_text(text: str, max_len: int = 300) -> list[str]

Split text into chunks by paragraphs and sentences.

This function intelligently splits long text into smaller chunks suitable for TTS processing, respecting paragraph and sentence boundaries.

Parameters:

Name Type Description Default
text str

Input text to chunk

required
max_len int

Maximum length of each chunk in characters (default: 300)

300

Returns:

Type Description
list[str]

List of text chunks

Example
text = "This is a long paragraph. It has multiple sentences. " * 10
chunks = chunk_text(text, max_len=100)
for chunk in chunks:
    print(f"Chunk ({len(chunk)} chars): {chunk[:50]}...")
Source code in supertonic/utils.py
def chunk_text(text: str, max_len: int = 300) -> list[str]:
    """
    Split text into chunks by paragraphs and sentences.

    This function intelligently splits long text into smaller chunks suitable
    for TTS processing, respecting paragraph and sentence boundaries.

    Args:
        text: Input text to chunk
        max_len: Maximum length of each chunk in characters (default: 300)

    Returns:
        List of text chunks

    Example:
        ```python
        text = "This is a long paragraph. It has multiple sentences. " * 10
        chunks = chunk_text(text, max_len=100)
        for chunk in chunks:
            print(f"Chunk ({len(chunk)} chars): {chunk[:50]}...")
        ```
    """
    # Validate minimum chunk length
    if max_len < 10:
        raise ValueError(
            f"max_len must be at least 10, got {max_len}. "
            f"Very small chunks may produce poor quality speech."
        )

    # Split by paragraph (two or more newlines)
    paragraphs = [p.strip() for p in re.split(r"\n\s*\n+", text.strip()) if p.strip()]

    chunks = []

    for paragraph in paragraphs:
        paragraph = paragraph.strip()
        if not paragraph:
            continue

        # Split by sentence boundaries (period, question mark, exclamation mark followed by space)
        # But exclude common abbreviations like Mr., Mrs., Dr., etc. and single capital letters
        sentences = re.split(_COMMON_ABBREVIATIONS_PATTERN, paragraph)

        current_chunk = ""

        for sentence in sentences:
            # Skip empty sentences to prevent empty chunks
            # Strip once and reuse the result
            sentence_stripped = sentence.strip()
            if not sentence_stripped:
                continue

            if len(current_chunk) + len(sentence_stripped) + 1 <= max_len:
                current_chunk += (" " if current_chunk else "") + sentence_stripped
            else:
                if current_chunk:
                    chunks.append(current_chunk.strip())
                current_chunk = sentence_stripped

        # Add final chunk if not empty
        if current_chunk and current_chunk.strip():
            chunks.append(current_chunk.strip())

    return chunks