import argparse import os from concurrent.futures import ThreadPoolExecutor import librosa import pyloudnorm as pyln import soundfile from tqdm import tqdm from common.log import logger from common.stdout_wrapper import SAFE_STDOUT from config import config DEFAULT_BLOCK_SIZE: float = 0.400 # seconds class BlockSizeException(Exception): pass def normalize_audio(data, sr): meter = pyln.Meter(sr, block_size=DEFAULT_BLOCK_SIZE) # create BS.1770 meter try: loudness = meter.integrated_loudness(data) except ValueError as e: raise BlockSizeException(e) # logger.info(f"loudness: {loudness}") data = pyln.normalize.loudness(data, loudness, -23.0) return data def process(item): spkdir, wav_name, args = item wav_path = os.path.join(args.in_dir, spkdir, wav_name) if os.path.exists(wav_path) and wav_path.lower().endswith(".wav"): wav, sr = librosa.load(wav_path, sr=args.sr) if args.normalize: try: wav = normalize_audio(wav, sr) except BlockSizeException: logger.info( f"Skip normalize due to less than {DEFAULT_BLOCK_SIZE} second audio: {wav_path}" ) if args.trim: wav, _ = librosa.effects.trim(wav, top_db=30) soundfile.write(os.path.join(args.out_dir, spkdir, wav_name), wav, sr) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument( "--sr", type=int, default=config.resample_config.sampling_rate, help="sampling rate", ) parser.add_argument( "--in_dir", "-i", type=str, default=config.resample_config.in_dir, help="path to source dir", ) parser.add_argument( "--out_dir", "-o", type=str, default=config.resample_config.out_dir, help="path to target dir", ) parser.add_argument( "--num_processes", type=int, default=4, help="cpu_processes", ) parser.add_argument( "--normalize", action="store_true", default=False, help="loudness normalize audio", ) parser.add_argument( "--trim", action="store_true", default=False, help="trim silence (start and end only)", ) args, _ = parser.parse_known_args() # autodl 无卡模式会识别出46个cpu if args.num_processes == 0: processes = cpu_count() - 2 if cpu_count() > 4 else 1 else: processes = args.num_processes tasks = [] for dirpath, _, filenames in os.walk(args.in_dir): # 子级目录 spk_dir = os.path.relpath(dirpath, args.in_dir) spk_dir_out = os.path.join(args.out_dir, spk_dir) if not os.path.isdir(spk_dir_out): os.makedirs(spk_dir_out, exist_ok=True) for filename in filenames: if filename.lower().endswith(".wav"): twople = (spk_dir, filename, args) tasks.append(twople) if len(tasks) == 0: logger.error(f"No wav files found in {args.in_dir}") raise ValueError(f"No wav files found in {args.in_dir}") # pool = Pool(processes=processes) # for _ in tqdm( # pool.imap_unordered(process, tasks), file=SAFE_STDOUT, total=len(tasks) # ): # pass # pool.close() # pool.join() with ThreadPoolExecutor(max_workers=processes) as executor: _ = list( tqdm( executor.map(process, tasks), total=len(tasks), file=SAFE_STDOUT, ) ) logger.info("Resampling Done!")