PlayDiffusion

repository·main·Indexed 19 days ago

https://github.com/playht/playdiffusion

A diffusion-based speech editing model for high-quality audio inpainting and text-to-speech (TTS). It utilizes a Masked Generative Codec Transformer (MaskGCT) with a DiffLlama backbone and BigVGAN decoder to modify specific audio segments without the artifacts or prosody shifts common in autoregressive models. The library includes tools for speech tokenization via XLS-R 1B, iterative decoding, and audio preprocessing.

Tokens
5.5K
Snippets
15
Records
19
Agent score
69%

What's inside playdiffusion

  1. How the PlayDiffusion model works

    main

    PlayDiffusion is a non-autoregressive diffusion-based approach designed for high-quality audio speech editing (inpainting). Unlike autoregressive models that struggle with boundary artifacts when modifying audio, PlayDiffusion uses a diffusion process to denoise masked regions while preserving surrounding context.

    The Workflow:

    1. Encoding: An audio sequence is encoded into discrete tokens.
    2. Masking: The specific segment intended for modification is masked.
    3. Denoising: A diffusion model, conditioned on the updated text, denoises the masked region. This ensures smooth transitions and consistent speaker characteristics.
    4. Decoding: The resulting token sequence is transformed back into a waveform using a BigVGAN decoder model.

    This approach allows for fine-grained speech modification (e.g., changing a specific word) without regenerating the entire sentence or causing prosody mismatches.

  2. PlayDiffusion inference and iterative decoding process

    main

    During inference, PlayDiffusion uses an iterative decoding process starting from a fully masked token sequence. The process follows these steps:

    1. Preliminary Prediction: The model generates an initial prediction ($\hat{X}_0$) conditioned on the current masked audio and textual input.
    2. Confidence Scoring: Tokens are assigned confidence scores. Newly predicted (previously masked) tokens get a score based on their predicted probabilities. Tokens that were previously determined retain a confidence score of $1$.
    3. Adaptive Remasking: Using a progressively decreasing schedule ($\gamma$), the lowest-confidence tokens are selected for remasking in the next iteration. The number of tokens to remask decreases over time, focusing refinement on high-uncertainty areas.

    This iterative refinement continues until the decoding steps are complete, resulting in coherent audio.

  3. Run PlayDiffusion using Docker or Podman

    main

    You can containerize PlayDiffusion using Docker or Podman. Ensure you mount your Hugging Face and Whisper caches to avoid re-downloading models. The demo runs on port 7860.

    Using Podman

    podman build -t playdiffusion-py:latest .
    
    podman run -it --rm \
      --device nvidia.com/gpu=all \
      --network=host \
      -v $HOME/.cache/huggingface:/app/.cache/huggingface \
      -v $HOME/.cache/whisper:$HOME/.cache/whisper \
      -p 7860:7860 \
      playdiffusion-py:latest

    Using Docker

    docker build -t playdiffusion-py:latest .
    
    docker run -it --rm \
      --gpus all \
      --network=host \
      -v $HOME/.cache/huggingface:/app/.cache/huggingface \
      -v $HOME/.cache/whisper:$HOME/.cache/whisper \
      -p 7860:7860 \
      playdiffusion-py:latest
    podman build -t playdiffusion-py:latest .
    
    podman run -it --rm \
      --device nvidia.com/gpu=all \
      --network=host \
      -v $HOME/.cache/huggingface:/app/.cache/huggingface \
      -v $HOME/.cache/whisper:$HOME/.cache/whisper \
      -p 7860:7860 \
      playdiffusion-py:latest
  4. Install PlayDiffusion via pip or uv

    main

    To use PlayDiffusion locally, you must set the OPENAI_API_KEY environment variable for Automatic Speech Recognition (ASR) and word timings. Follow these steps to set up a virtual environment and install the package with the demo extra:

    1. Create a virtual environment: python3.11 -m venv .venv or uv venv
    2. Activate the environment: source .venv/bin/activate
    3. Install the package and demo dependencies: pip install '.[demo]' or uv sync --extra demo
    4. Run the Gradio demo: python demo/gradio-demo.py or uv run demo/gradio-demo.py
    # Install with pip
    pip install '.[demo']
    
    # Or with uv
    uv sync --extra demo
    
    # Run the demo
    python demo/gradio-demo.py
    # or
    uv run demo/gradio-demo.py
  5. Configure SpeechTokenizer initialization

    main

    When initializing SpeechTokenizer, you can specify the following:

    • checkpoint (str): Path to the XLS-R encoder checkpoint. Defaults to "data/checkpoints/xlsr2_1b_v2_custom.pt".
    • kmeans_layer_checkpoint (str): Path to the pre-trained Kmeans .npy file. Defaults to "data/checkpoints/kmeans_10k.npy".
    • dtype (DataType): The precision for the encoder and kmeans layers. Defaults to torch.float16.
    • device (Device): The target device.
  6. Configure SpeechEncoder initialization

    main

    When initializing SpeechEncoder, you can control the model depth and precision:

    • checkpoint (str): Path to the XLS-R checkpoint. Defaults to "data/checkpoints/xlsr2_1b_v2_custom.pt".
    • max_layer (int): The number of layers to load. If set (e.g., 35), the model will only load up to that layer and disable the final layer_norm to preserve intermediate representation quality.
    • device (Device): The target device (e.g., torch.device('cuda')).
    • dtype (DataType): The precision (e.g., torch.float32 or torch.float16).
    • strict (bool): If max_layer is used, this is automatically set to False to allow loading partial state dicts.
    • eval (bool): If True, sets the model to evaluation mode.
  7. Load an XLS-R encoder with load_xlsr_encoder()

    main

    Use load_xlsr_encoder() to initialize a Wav2Vec2Model along with its corresponding Wav2Vec2Config and Wav2Vec2EncoderConfig. This function is designed to build the correct configurations for the XLS-R 1B v2 model architecture.

    To optimize memory usage and avoid loading unnecessary weights, you can specify a max_layer parameter to truncate the number of transformer encoder layers.

    from src.playdiffusion.models.speech_tokenizer.xlsr_encoder import load_xlsr_encoder
    from fairseq2.typing import Device, DataType
    import torch
    
    # Define device and dtype
    device = Device("cuda") if torch.cuda.is_available() else Device("cpu")
    dtype = torch.float16
    
    # Load the model, potentially limiting the number of layers to 35 (default)
    model, config, encoder_config = load_xlsr_encoder(
        device=device, 
        dtype=dtype, 
        max_layer=35
    )
  8. Use SpeechEncoder to extract intermediate latents

    main

    The SpeechEncoder is a wrapper for the XLS-R 1B model designed to extract intermediate representations (latents) from a waveform. It allows loading only a subset of the model's layers (up to max_layer) to save memory and compute.

    When using max_layer, the model automatically disables the final layer_norm to ensure the intermediate representations are not incorrectly normalized for the final layer's distribution.

    from playdiffusion.models.speech_tokenizer.speech_tokenizer import SpeechEncoder
    import torch
    
    # Initialize encoder with a specific checkpoint and layer limit
    encoder = SpeechEncoder(
        checkpoint="data/checkpoints/xlsr2_1b_v2_custom.pt",
        max_layer=35,
        device="cuda",
        dtype=torch.float32
    )
    
    # Input batch must be a SequenceBatch (from fairseq2)
    # encoder(batch) returns (encoder_output, padding_mask)
    latents, padding_mask = encoder(batch)
  9. Generate audio using MaskGCT (TTS or Inpainting)

    main

    The generate method is the primary entrypoint for inference. It supports two modes:

    1. Text-to-Speech (TTS): Provide text_tokens and target_len. Ensure code, start_frame, and end_frame are None.
    2. Audio Inpainting: Provide text_tokens, target_len, and a code tensor representing the existing audio. You must specify start_frame and end_frame to define the segment to be inpainted.

    Parameters

    • text_tokens (torch.Tensor): Shape (B, T). The input text token IDs.
    • target_len (int): The length of the audio segment to generate.
    • n_timesteps (int): Number of diffusion steps. Default: 40.
    • init_temp (float): Initial temperature for sampling. Default: 1.5.
    • init_diversity (float): Initial diversity factor. Default: 1.
    • guidance (float): Classifier-free guidance scale. Default: 0.
    • rescale_cfg (float): CFG rescaling factor. Default: 0.75.
    • topk (int): Top-k sampling threshold. Default: 20.
    • code (torch.Tensor, optional): The existing audio codebook tokens for inpainting. Shape (B, T).
    • start_frame (int, optional): The starting index of the inpainting region in code.
    • end_frame (int, optional): The ending index of the inpainting region in code.

    Returns a torch.Tensor of generated audio codebook tokens.

    # Example: TTS Generation
    # text_tokens shape: (Batch, SeqLen)
    target_len = 500
    text_tokens = torch.tensor([[1, 2, 3, 4]]) 
    
    generated_codes = model.generate(
        text_tokens=text_tokens,
        target_len=target_len,
        n_timesteps=40,
        guidance=3.0
    )
    
    # Example: Inpainting
    # code shape: (Batch, TotalSeqLen)
    # start_frame/end_frame define the gap to fill
    existing_code = torch.tensor([[...]]) 
    
    generated_codes = model.generate(
        text_tokens=text_tokens,
        target_len=100,
        code=existing_code,
        start_frame=50,
        end_frame=150
    )
  10. Initialize and use the MaskGCT model

    main

    The MaskGCT class implements a Masked Generative Codec Transformer (MaskGCT) for audio inpainting and text-to-speech tasks. It uses a DiffLlama backbone to predict masked audio tokens based on text guidance.

    Initialization Parameters

    • vocab_text (int): Size of the text vocabulary (includes BOS/EOS). Default: 13512.
    • vocab_audio (int): Size of the audio codebook. Default: 10000.
    • num_layers (int): Number of transformer layers. Default: 20.
    • num_heads (int): Number of attention heads. Default: 16.
    • num_kv_heads (int): Number of KV heads. Default: 16.
    • embed_dim (int): Embedding dimension. Default: 1024.
    • intermediate_dim (int): Dimension of the intermediate feed-forward layer. Default: 4096.
    • max_seq_len (int): Maximum sequence length. Default: 4096.
    • attn_dropout (float): Attention dropout rate. Default: 0.0.
    • norm_eps (float): Normalization epsilon. Default: 1e-5.
    • rope_base (float): RoPE base frequency. Default: 500000.0.

    Loading from Checkpoint

    Use load_maskgct_inpainter to instantiate the model with configuration and weights from a saved checkpoint file.

    from playdiffusion.models.inpainter.masklm_text import load_maskgct_inpainter
    
    model = load_maskgct_inpainter(
        checkpoint='path/to/checkpoint.pt', 
        device='cuda'
    )
  11. Measure execution time with Timer

    main

    The Timer class provides a simple way to measure and print the elapsed time between consecutive calls or from the moment of initialization.

    Usage:

    1. Instantiate Timer().
    2. Call the instance as a function with a description string: timer("task_name").
    3. The timer prints the elapsed time in milliseconds (ms) and stores it in an internal dictionary.
    4. Use get_times() to retrieve the dictionary of all recorded durations.
    from playdiffusion.utils.audio_utils import Timer
    
    timer = Timer()
    
    # Perform some operation
    dosomething()
    timer("operation_1") # Prints: operation_1 time: X.X ms
    
    # Perform another operation
    dosomething_else()
    timer("operation_2") # Prints: operation_2 time: X.X ms
    
    # Retrieve all recorded times
    all_times = timer.get_times()
  12. Load and preprocess audio with load_audio()

    main

    Use load_audio(audio_path, device) to load an audio file from a path, convert it to a PyTorch tensor, normalize it, and move it to the specified device.

    Requirements & Behavior:

    • Sample Rate: The audio must have a sample rate of at least 16kHz (ideally 24kHz). If the sample rate is lower, an exception is raised.
    • Mono Conversion: If the input audio is multi-channel, it is automatically converted to mono by averaging the channels.
    • Normalization: The function automatically calculates a normalization factor based on the data type (float or integer) to ensure the output tensor is scaled correctly.
    • Output: Returns a tuple containing the torch_audio tensor and the sr (sample rate).
    from playdiffusion.utils.audio_utils import load_audio
    
    # Load audio and move to GPU
    device = "cuda"
    torch_audio, sr = load_audio("path/to/audio.wav", device)
    
    print(f"Sample rate: {sr}")
    print(f"Audio shape: {torch_audio.shape}")