import os import re import glob import json import torch import funcy import faiss import IPython import numpy as np import torchaudio import itertools from tqdm import tqdm from typing import List from dac.model.dac4 import DAC from dac.model.discriminator2 import Discriminator as Discriminator_import from dac.nn import loss as loss_import from dac.utils.accelerator import Accelerator from dac.utils import load_model import matplotlib.pyplot as plt from sklearn.preprocessing import StandardScaler from sklearn.cluster import KMeans, MiniBatchKMeans from suno_utils.models.musicfm.modeling_MusicFM import MusicFM_MERTLong from suno_utils.utils.s3 import read_from_s3 # from suno_utils.utils.text import ( # write_jsonl, # read_jsonl, # write_json, # read_json, # normalize_whitespace, # ) from transformers import WhisperProcessor, WhisperForConditionalGeneration SAMPLE_RATE = 48_000 VAE_DIM = 128 VAE_T_MEMMAP = 1000 USE_MERT = False MERT_DIM = 1024 MERT_T_MEMMAP = 250 NUM_FRAMES = int(10 * SAMPLE_RATE) BATCH_SIZE = 32 MAX_N_CHUNKS = 3 MAX_FILES_PER_SUBSET = 500000 USE_WHISPHER = True class SimpleAudioDataset(torch.utils.data.Dataset): def __init__(self, filepaths: List[str]): self.filepaths = filepaths def __len__(self): return len(self.filepaths) def __getitem__(self, idx): audio_file = self.filepaths[idx] num_frames = torchaudio.info(audio_file).num_frames if num_frames > NUM_FRAMES: start_frame = np.random.randint(0, num_frames - NUM_FRAMES - 1) end_frame = start_frame + NUM_FRAMES # load all the audio audio, sr = torchaudio.load( audio_file, frame_offset=start_frame, num_frames=NUM_FRAMES ) else: start_frame = 0 end_frame = num_frames audio, sr = torchaudio.load(audio_file) if audio.shape[0] != 2: audio = audio.repeat(2, 1) if audio.shape[-1] < NUM_FRAMES: audio = torch.cat( [audio, torch.zeros(2, NUM_FRAMES - audio.shape[-1])], dim=-1 ) assert sr == SAMPLE_RATE return audio, start_frame, end_frame def write_jsonl(data, filename): with open(filename, "w", encoding="utf-8") as f: for d in data: f.write(json.dumps(d, ensure_ascii=False) + "\n") class AudioDataset(torch.utils.data.Dataset): def __init__( self, filepaths: List[str], ): # check length of ecah file and create examples audio_chunks = [] print("Checking audio files...") for audio_file in tqdm(filepaths): audio, sr = torchaudio.load(audio_file) audio /= audio.abs().max().clamp(1e-4) num_frames = audio.shape[-1] num_chunks = num_frames // NUM_FRAMES audio_file_chunks = [] for i in range(num_chunks): start_frame = i * NUM_FRAMES end_frame = (i + 1) * NUM_FRAMES audio_chunk = audio[..., start_frame:end_frame] audio_chunk_energy = audio_chunk.pow(2).mean().item() if audio_chunk_energy > 1e-3 and np.random.rand() > 0.5: audio_file_chunks.append((audio_file, start_frame, end_frame)) if len(audio_file_chunks) >= MAX_N_CHUNKS: break audio_chunks.extend(audio_file_chunks) print("Total", len(filepaths), "chunks", len(audio_chunks)) print(np.random.choice(filepaths, 5)) self.filepaths = filepaths self.audio_chunks = audio_chunks def __len__(self): return len(self.audio_chunks) def __getitem__(self, idx): audio_file, start_frame, end_frame = self.audio_chunks[idx] # load all the audio audio, sr = torchaudio.load( audio_file, frame_offset=start_frame, num_frames=NUM_FRAMES ) if audio.shape[0] != 2: audio = audio.repeat(2, 1) if audio.shape[-1] < NUM_FRAMES: audio = torch.cat( [audio, torch.zeros(2, NUM_FRAMES - audio.shape[-1])], dim=-1 ) assert sr == SAMPLE_RATE return audio if __name__ == "__main__": # load VAE device = "cuda:0" # checkpoint_filepath = ( # "/app/suno/christian/checkpoints/dac/100hz_vae_peaq_kl_0.1/best/dac/weights.pth" # ) checkpoint_filepath = "s3://suno-data/christian/100hz_vae_peaq_kl_0.005.pth" load_f = funcy.partial(torch.load, map_location="cpu") if checkpoint_filepath.startswith("s3://"): sd = read_from_s3(checkpoint_filepath, read_f=load_f) else: sd = load_f(checkpoint_filepath) sd["metadata"]["kwargs"] = { k: v for k, v in sd["metadata"]["kwargs"].items() if k in DAC.__init__.__code__.co_varnames } model_100hz = DAC(**sd["metadata"]["kwargs"]) model_100hz.load_state_dict(sd["state_dict"]) model_100hz.eval() model_100hz.to(device) # load MERT if USE_MERT: model_filepath = "s3://suno-data/minz/models/musicfm_concat_epoch=51.pt" centroids_filepath = "s3://suno-data/minz/models/musicfm_concat_centroids.npy" mert_model = MusicFM_MERTLong( is_flash=False, stat_path="s3://suno-data/minz/models/mertlong_stats.json", model_path=model_filepath, ) mert_model.cuda() mert_model.eval() for name, param in mert_model.named_parameters(): param.requires_grad = False # load whisper model for transcription if USE_WHISPHER: whisper_processor = WhisperProcessor.from_pretrained("openai/whisper-base") whisper_model = WhisperForConditionalGeneration.from_pretrained( "openai/whisper-base" ) whisper_model.cuda() root_dir = "/app/suno/christian/data/tiktok_covers_lyrics_48khz" # root_dir = "/app/suno/data/audio_2ch_48khz_lg" dataset_name = "tiktok_covers" # set up train and test splits embed_dir = "/app/suno/christian/data/suno_diffusion_tiktok_covers" os.makedirs(embed_dir, exist_ok=True) for subset_name in ["train"]: subset_filepaths = glob.glob(os.path.join(root_dir, subset_name, "*.wav")) subset_filepaths = subset_filepaths dataset = SimpleAudioDataset(subset_filepaths) dataloader = torch.utils.data.DataLoader( dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=32 ) # output memmap vae_example_size = VAE_DIM * VAE_T_MEMMAP mert_example_size = MERT_DIM * MERT_T_MEMMAP vae_mm_write_idx = 0 mert_mm_write_idx = 0 print("Creating memmap...") out_vae_mm = np.memmap( f"{embed_dir}/vae_100hz_kl_0.1_{subset_name}.bin", dtype=np.float32, mode="w+", shape=(len(dataset) * vae_example_size), ) if USE_MERT: out_mert_mm = np.memmap( f"{embed_dir}/mert_{subset_name}.bin", dtype=np.float32, mode="w+", shape=(len(dataset) * mert_example_size), ) audios = [] metas_out = [] global_idx = 0 print("Encoding...") for batch in tqdm(dataloader): audio_chunks, start_fames, end_frames = batch # transcribe if USE_WHISPHER: audios_16k = [] audio_chunks_16k = torchaudio.functional.resample( audio_chunks.mean(dim=1, keepdim=False), 48000, 16000 ) # convert audio to list of mono numpy arrays at 16 khz for audio in audio_chunks_16k: audios_16k.append(audio.numpy()) input_features = whisper_processor( audios_16k, sampling_rate=16000, return_tensors="pt", ).input_features input_features = input_features.cuda() # Generate token ids predicted_ids = whisper_model.generate(input_features) # Decode token ids to text transcriptions = whisper_processor.batch_decode( predicted_ids, skip_special_tokens=True ) audios = audio_chunks.to(device) if USE_MERT: with torch.no_grad(): audios_24k_mono = torchaudio.functional.resample( audios.mean(dim=1, keepdim=False), 48_000, 24_000 ) out = mert_model.get_latent(audios_24k_mono) mert_z = out with torch.no_grad(): out = model_100hz.encode(audios) vae_z = out["z"] # store metas for bidx in range(vae_z.shape[0]): meta = {} meta["audio_file"] = subset_filepaths[global_idx] meta["dataset"] = dataset_name if USE_WHISPHER: meta["lyrics"] = transcriptions[bidx] print(transcriptions[bidx]) meta["start_frame"] = start_fames[bidx].item() meta["end_frame"] = end_frames[bidx].item() meta["start_sec"] = meta["start_frame"] / SAMPLE_RATE meta["end_sec"] = meta["end_frame"] / SAMPLE_RATE metas_out.append(meta) global_idx += 1 # store results into memmap for bidx in range(vae_z.shape[0]): # write vae embed to memmap vae_embed = vae_z[bidx : bidx + 1, ...].reshape(-1).cpu().numpy() # check for nan in embed if np.isnan(vae_embed).any(): print("Found NaN in VAE embed") continue out_vae_mm[vae_mm_write_idx : vae_mm_write_idx + vae_example_size] = ( vae_embed ) vae_mm_write_idx += vae_example_size # write mert embed to memmap if USE_MERT: mert_embed = mert_z[bidx : bidx + 1, ...].reshape(-1).cpu().numpy() # check for nan in embed if np.isnan(mert_embed).any(): print("Found NaN in MERT embed") continue out_mert_mm[ mert_mm_write_idx : mert_mm_write_idx + mert_example_size ] = mert_embed mert_mm_write_idx += mert_example_size # flush out_vae_mm.flush() del out_vae_mm # save out metas write_jsonl(metas_out, f"{embed_dir}/{subset_name}_metas.jsonl") if USE_MERT: out_mert_mm.flush() del out_mert_mm