CoolFace
Apppublic

cocktailpeanut/DiffRhythm

sourceHugging Faceapache-2.0updated 1y agoView on Hugging Face
10likes
infer.py189 linesDownload Raw Back to infer
1import torch2import torchaudio3from einops import rearrange4import argparse5import json6import os7from tqdm import tqdm8import random9import numpy as np10import time11import io12import gc13import pydub14 15from diffrhythm.infer.infer_utils import (16    get_reference_latent,17    get_lrc_token,18    get_style_prompt,19    get_audio_style_prompt,20    prepare_model,21    get_negative_style_prompt22)23 24def decode_audio(latents, vae_model, chunked=False, overlap=32, chunk_size=128):25    downsampling_ratio = 204826    io_channels = 227    if not chunked:28        # default behavior. Decode the entire latent in parallel29        return vae_model.decode_export(latents)30    else:31        # chunked decoding32        hop_size = chunk_size - overlap33        total_size = latents.shape[2]34        batch_size = latents.shape[0]35        chunks = []36        i = 037        for i in range(0, total_size - chunk_size + 1, hop_size):38            chunk = latents[:,:,i:i+chunk_size]39            chunks.append(chunk)40        if i+chunk_size != total_size:41            # Final chunk42            chunk = latents[:,:,-chunk_size:]43            chunks.append(chunk)44        chunks = torch.stack(chunks)45        num_chunks = chunks.shape[0]46        # samples_per_latent is just the downsampling ratio47        samples_per_latent = downsampling_ratio48        # Create an empty waveform, we will populate it with chunks as decode them49        y_size = total_size * samples_per_latent50        y_final = torch.zeros((batch_size,io_channels,y_size)).to(latents.device)51        for i in range(num_chunks):52            x_chunk = chunks[i,:]53            # decode the chunk54            y_chunk = vae_model.decode_export(x_chunk)55            # figure out where to put the audio along the time domain56            if i == num_chunks-1:57                # final chunk always goes at the end58                t_end = y_size59                t_start = t_end - y_chunk.shape[2]60            else:61                t_start = i * hop_size * samples_per_latent62                t_end = t_start + chunk_size * samples_per_latent63            #  remove the edges of the overlaps64            ol = (overlap//2) * samples_per_latent65            chunk_start = 066            chunk_end = y_chunk.shape[2]67            if i > 0:68                # no overlap for the start of the first chunk69                t_start += ol70                chunk_start += ol71            if i < num_chunks-1:72                # no overlap for the end of the last chunk73                t_end -= ol74                chunk_end -= ol75            # paste the chunked audio into our y_final output audio76            y_final[:,:,t_start:t_end] = y_chunk[:,:,chunk_start:chunk_end]77        return y_final78 79def inference(cfm_model, vae_model, cond, text, duration, style_prompt, negative_style_prompt, steps, cfg_strength, sway_sampling_coef, start_time, file_type, vocal_flag, odeint_method):80 81    with torch.inference_mode():82        print(">1")83        generated, _ = cfm_model.sample(84            cond=cond,85            text=text,86            duration=duration,87            style_prompt=style_prompt,88            negative_style_prompt=negative_style_prompt,89            steps=steps,90            cfg_strength=cfg_strength,91            sway_sampling_coef=sway_sampling_coef,92            start_time=start_time,93            vocal_flag=vocal_flag,94            odeint_method=odeint_method,95        )96        if torch.cuda.is_available():97            torch.cuda.empty_cache()98        elif torch.mps.is_available():99            torch.mps.empty_cache()100        gc.collect()101 102        103        print(">2")104        generated = generated.to(torch.float32)105        print(">3")106        latent = generated.transpose(1, 2) # [b d t]107        print(">4")108        output = decode_audio(latent, vae_model, chunked=True)109        print(">5")110 111        del latent, generated112        if torch.cuda.is_available():113            torch.cuda.empty_cache()114        elif torch.mps.is_available():115            torch.mps.empty_cache()116        gc.collect()117 118        print(">6")119 120        # Rearrange audio batch to a single sequence121        output = rearrange(output, "b d n -> d (b n)")122        output_tensor = output.to(torch.float32).div(torch.max(torch.abs(output))).clamp(-1, 1).cpu()123        output_np = output_tensor.numpy().T.astype(np.float32)124        125        if file_type == 'wav':126            return (44100, output_np)127        else:128            buffer = io.BytesIO()129            output_np = np.int16(output_np * 2**15)130            song = pydub.AudioSegment(output_np.tobytes(), frame_rate=44100, sample_width=2, channels=2)131            if file_type == 'mp3':132                song.export(buffer, format="mp3", bitrate="320k")133            else:134                song.export(buffer, format="ogg", bitrate="320k")135            return buffer.getvalue()136    137        138if __name__ == "__main__":139    parser = argparse.ArgumentParser()140    parser.add_argument('--lrc-path', type=str, default="example/eg.lrc") # lyrics of target song141    parser.add_argument('--ref-audio-path', type=str, default="example/eg.mp3") # reference audio as style prompt for target song142    parser.add_argument('--audio-length', type=int, default=95) # length of target song143    parser.add_argument('--output-dir', type=str, default="example/output")144    args = parser.parse_args()145    146    device = "cpu"147    if torch.cuda.is_available():148        device = "cuda"149    elif torch.mps.is_available():150        device = "mps"151    152    audio_length = args.audio_length153    if audio_length == 95:154        max_frames = 2048155    elif audio_length == 285:156        max_frames = 6144157    158    cfm, tokenizer, muq, vae = prepare_model(device)159    160    with open(args.lrc_path, 'r') as f:161        lrc = f.read()162    lrc_prompt, start_time = get_lrc_token(lrc, tokenizer, device)163    164    style_prompt = get_style_prompt(muq, args.ref_audio_path)165    166    negative_style_prompt = get_negative_style_prompt(device)167    168    latent_prompt = get_reference_latent(device, max_frames)169    170    s_t = time.time()171    generated_song = inference(cfm_model=cfm, 172                               vae_model=vae, 173                               cond=latent_prompt, 174                               text=lrc_prompt, 175                               duration=max_frames, 176                               style_prompt=style_prompt,177                               negative_style_prompt=negative_style_prompt,178                               start_time=start_time179                               )180    e_t = time.time() - s_t181    print(f"inference cost {e_t} seconds")182    183    output_dir = args.output_dir184    os.makedirs(output_dir, exist_ok=True)185    186    output_path = os.path.join(output_dir, "output.wav")187    torchaudio.save(output_path, generated_song, sample_rate=44100)188    189