From 2880e3a6f88af2bd4128ec4724c38733aec3121e Mon Sep 17 00:00:00 2001 From: Downupanddownup Date: Fri, 26 Apr 2024 13:25:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=80=A7=E8=83=BD=E7=9B=91?= =?UTF-8?q?=E6=8E=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ref_Audio_Selector/common/time_util.py | 71 ++++++++++++ Ref_Audio_Selector/config.ini | 4 + .../config_param/config_params.py | 4 + Ref_Audio_Selector/config_param/log_config.py | 39 +++++++ .../ref_audio_selector_webui.py | 103 +++++++++++++----- .../tool/asr/funasr_asr_multi_level_dir.py | 26 +---- Ref_Audio_Selector/tool/audio_inference.py | 2 + Ref_Audio_Selector/tool/audio_similarity.py | 6 +- .../speaker_verification/voice_similarity.py | 2 + .../tool/text_comparison/asr_text_process.py | 2 + 10 files changed, 202 insertions(+), 57 deletions(-) create mode 100644 Ref_Audio_Selector/common/time_util.py create mode 100644 Ref_Audio_Selector/config_param/log_config.py diff --git a/Ref_Audio_Selector/common/time_util.py b/Ref_Audio_Selector/common/time_util.py new file mode 100644 index 0000000..82d63b8 --- /dev/null +++ b/Ref_Audio_Selector/common/time_util.py @@ -0,0 +1,71 @@ +import time +from Ref_Audio_Selector.config_param.log_config import p_logger +import Ref_Audio_Selector.config_param.config_params as params + + +def timeit_decorator(func): + """ + 装饰器,用于计算被装饰函数的执行时间。 + + 参数: + func (function): 要计时的函数。 + + 返回: + function: 包含计时功能的新函数。 + """ + + def wrapper(*args, **kwargs): + if params.time_log_print_type != 'file': + return func(*args, **kwargs) + + start_time = time.perf_counter() # 使用 perf_counter 获取高精度计时起点 + + func_result = func(*args, **kwargs) # 执行原函数 + + end_time = time.perf_counter() # 获取计时终点 + elapsed_time = end_time - start_time # 计算执行耗时 + + # 记录日志内容 + log_message = f"{func.__name__} 执行耗时: {elapsed_time:.6f} 秒" + p_logger.info(log_message) + + return func_result + + return wrapper + + +def time_monitor(func): + """ + 返回结果,追加时间 + """ + + def wrapper(*args, **kwargs): + + start_time = time.perf_counter() # 使用 perf_counter 获取高精度计时起点 + + func_result = func(*args, **kwargs) # 执行原函数 + + end_time = time.perf_counter() # 获取计时终点 + elapsed_time = end_time - start_time # 计算执行耗时 + + return elapsed_time, func_result + + return wrapper + + +# 使用装饰器 +@timeit_decorator +def example_function(n): + time.sleep(n) # 假设这是需要计时的函数,这里模拟耗时操作 + return n * 2 + + +def example_function2(n): + time.sleep(n) # 假设这是需要计时的函数,这里模拟耗时操作 + return n * 2 + + +if __name__ == "__main__": + # 调用经过装饰的函数 + # result = example_function(2) + print(time_monitor(example_function2)(2)) diff --git a/Ref_Audio_Selector/config.ini b/Ref_Audio_Selector/config.ini index 81bcb96..0ba2aa2 100644 --- a/Ref_Audio_Selector/config.ini +++ b/Ref_Audio_Selector/config.ini @@ -1,6 +1,10 @@ # config.ini [Base] +# 函数时间消耗日志打印类型 file 打印到文件; close 关闭 +time_log_print_type = file +# 函数时间消耗日志保存目录路径 +time_log_print_dir = Ref_Audio_Selector/log/performance # 参考音频目录 reference_audio_dir = refer_audio diff --git a/Ref_Audio_Selector/config_param/config_params.py b/Ref_Audio_Selector/config_param/config_params.py index 015a5e7..050bb1d 100644 --- a/Ref_Audio_Selector/config_param/config_params.py +++ b/Ref_Audio_Selector/config_param/config_params.py @@ -3,6 +3,10 @@ import Ref_Audio_Selector.config_param.config_manager as config_manager config = config_manager.get_config() # [Base] +# 函数时间消耗日志打印类型 file 打印到文件; close 关闭 +time_log_print_type = config.get_base('time_log_print_type') +# 函数时间消耗日志保存目录路径 +time_log_print_dir = config.get_base('time_log_print_dir') # 参考音频目录 reference_audio_dir = config.get_base('reference_audio_dir') diff --git a/Ref_Audio_Selector/config_param/log_config.py b/Ref_Audio_Selector/config_param/log_config.py new file mode 100644 index 0000000..249eba5 --- /dev/null +++ b/Ref_Audio_Selector/config_param/log_config.py @@ -0,0 +1,39 @@ +import logging +import datetime +import Ref_Audio_Selector.config_param.config_params as params + + +def setup_logging(): + # 获取当前日期,用于文件名和日志内容 + current_date = datetime.datetime.now().strftime('%Y-%m-%d') + + # 创建一个用于常规日志的处理器 + general_handler = logging.FileHandler('general.log', mode='a', encoding='utf-8') + general_handler.setLevel(logging.INFO) + general_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') + general_handler.setFormatter(general_formatter) + + # 创建一个专用于性能监控日志的处理器 + performance_handler = logging.FileHandler( + f"{params.time_log_print_dir}/{current_date}.log", mode='a', encoding='utf-8') + performance_handler.setLevel(logging.INFO) + performance_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') + performance_handler.setFormatter(performance_formatter) + + # 配置一个常规的logger + general_logger = logging.getLogger('general') + general_logger.setLevel(logging.INFO) + general_logger.addHandler(general_handler) + + # 配置一个专门用于性能监控的logger + performance_logger = logging.getLogger('performance') + performance_logger.setLevel(logging.INFO) + performance_logger.addHandler(performance_handler) + + # 配置根logger,以防万一 + logging.basicConfig(level=logging.WARNING, handlers=[general_handler]) + + return general_logger, performance_logger + + +logger, p_logger = setup_logging() \ No newline at end of file diff --git a/Ref_Audio_Selector/ref_audio_selector_webui.py b/Ref_Audio_Selector/ref_audio_selector_webui.py index 710aa3c..0a12785 100644 --- a/Ref_Audio_Selector/ref_audio_selector_webui.py +++ b/Ref_Audio_Selector/ref_audio_selector_webui.py @@ -9,6 +9,8 @@ import Ref_Audio_Selector.tool.audio_config as audio_config import Ref_Audio_Selector.tool.delete_inference_with_ref as delete_inference_with_ref import Ref_Audio_Selector.common.common as common import Ref_Audio_Selector.config_param.config_params as params +import Ref_Audio_Selector.common.time_util as time_util +from Ref_Audio_Selector.config_param.log_config import logger from tools.i18n.i18n import I18nAuto from config import python_exec, is_half from tools import my_utils @@ -16,11 +18,11 @@ from tools.asr.config import asr_dict from subprocess import Popen i18n = I18nAuto() +rw_param = params.config_manager.get_rw_param() p_similarity = None p_asr = None p_text_similarity = None -rw_param = params.config_manager.get_rw_param() # 校验基础信息 @@ -50,10 +52,13 @@ def convert_from_list(text_work_space_dir, text_role, text_list_input): ref_audio_all = os.path.join(base_role_dir, params.list_to_convert_reference_audio_dir) - text_convert_from_list_info = f"转换成功:生成目录{ref_audio_all}" + + time_consuming, _ = time_util.time_monitor(audio_similarity.convert_from_list)(text_list_input, ref_audio_all) + + text_convert_from_list_info = f"耗时:{time_consuming:0.1f}秒;转换成功:生成目录{ref_audio_all}" text_sample_dir = ref_audio_all - audio_similarity.convert_from_list(text_list_input, ref_audio_all) + # audio_similarity.convert_from_list(text_list_input, ref_audio_all) except Exception as e: traceback.print_exc() text_convert_from_list_info = f"发生异常:{e}" @@ -113,10 +118,17 @@ def sample(text_work_space_dir, text_role, text_sample_dir, text_base_voice_path raise Exception("每段随机抽样个数不能为空") ref_audio_dir = os.path.join(base_role_dir, params.reference_audio_dir) - text_sample_info = f"抽样成功:生成目录{ref_audio_dir}" - similarity_list, _, _ = start_similarity_analysis(base_role_dir, text_sample_dir, - text_base_voice_path, checkbox_similarity_output) + time_consuming, similarity_list, _, _ = ( + time_util.time_monitor(start_similarity_analysis)(base_role_dir, + text_sample_dir, + text_base_voice_path, + checkbox_similarity_output)) + + text_sample_info = f"耗时:{time_consuming:0.1f}秒;抽样成功:生成目录{ref_audio_dir}" + + # similarity_list, _, _ = start_similarity_analysis(base_role_dir, text_sample_dir, + # text_base_voice_path, checkbox_similarity_output) if similarity_list is None: raise Exception("相似度分析失败") @@ -136,9 +148,9 @@ def sample(text_work_space_dir, text_role, text_sample_dir, text_base_voice_path # 根据参考音频和测试文本,执行批量推理 def model_inference(text_work_space_dir, text_role, text_model_inference_voice_dir, text_url, text_text, text_ref_path, text_ref_text, text_emotion, - text_test_content): - text_work_space_dir, text_model_inference_voice_dir, text_test_content \ - = common.batch_clean_paths([text_work_space_dir, text_model_inference_voice_dir, text_test_content]) + text_test_content_dir): + text_work_space_dir, text_model_inference_voice_dir, text_test_content_dir \ + = common.batch_clean_paths([text_work_space_dir, text_model_inference_voice_dir, text_test_content_dir]) inference_dir = None text_asr_audio_dir = None @@ -151,7 +163,7 @@ def model_inference(text_work_space_dir, text_role, text_model_inference_voice_d raise Exception("推理服务请求地址不能为空") if text_text is None or text_text == '': raise Exception("文本参数名不能为空") - if text_test_content is None or text_test_content == '': + if text_test_content_dir is None or text_test_content_dir == '': raise Exception("待推理文本路径不能为空") if (text_ref_path is None or text_ref_path == '') and (text_ref_text is None or text_ref_text == '') and ( text_emotion is None or text_emotion == ''): @@ -160,18 +172,24 @@ def model_inference(text_work_space_dir, text_role, text_model_inference_voice_d inference_dir = os.path.join(base_role_dir, params.inference_audio_dir) text_asr_audio_dir = os.path.join(inference_dir, params.inference_audio_text_aggregation_dir) - text_model_inference_info = f"推理成功:生成目录{inference_dir}" url_composer = audio_inference.URLComposer(text_url, text_emotion, text_text, text_ref_path, text_ref_text) url_composer.is_valid() - text_list = common.read_text_file_to_list(text_test_content) + text_list = common.read_text_file_to_list(text_test_content_dir) if text_list is None or len(text_list) == 0: raise Exception("待推理文本内容不能为空") ref_audio_manager = common.RefAudioListManager(text_model_inference_voice_dir) if len(ref_audio_manager.get_audio_list()) == 0: raise Exception("待推理的参考音频不能为空") - audio_inference.generate_audio_files(url_composer, text_list, ref_audio_manager.get_ref_audio_list(), - inference_dir) + + time_consuming, _ = time_util.time_monitor(audio_inference.generate_audio_files)(url_composer, text_list, + ref_audio_manager.get_ref_audio_list(), + inference_dir) + + text_model_inference_info = f"耗时:{time_consuming:0.1f}秒;推理成功:生成目录{inference_dir}" + + # audio_inference.generate_audio_files(url_composer, text_list, ref_audio_manager.get_ref_audio_list(), + # inference_dir) except Exception as e: traceback.print_exc() text_model_inference_info = f"发生异常:{e}" @@ -198,10 +216,15 @@ def asr(text_work_space_dir, text_role, text_asr_audio_dir, dropdown_asr_model, raise Exception("asr模型大小不能为空") if dropdown_asr_lang is None or dropdown_asr_lang == '': raise Exception("asr语言不能为空") - asr_file = open_asr(text_asr_audio_dir, base_role_dir, dropdown_asr_model, dropdown_asr_size, - dropdown_asr_lang) + + time_consuming, asr_file = time_util.time_monitor(open_asr)(text_asr_audio_dir, base_role_dir, + dropdown_asr_model, dropdown_asr_size, + dropdown_asr_lang) + + # asr_file = open_asr(text_asr_audio_dir, base_role_dir, dropdown_asr_model, dropdown_asr_size, + # dropdown_asr_lang) text_text_similarity_analysis_path = asr_file - text_asr_info = f"asr成功:生成文件{asr_file}" + text_asr_info = f"耗时:{time_consuming:0.1f}秒;asr成功:生成文件{asr_file}" except Exception as e: traceback.print_exc() text_asr_info = f"发生异常:{e}" @@ -253,8 +276,13 @@ def text_similarity_analysis(text_work_space_dir, text_role, if text_text_similarity_analysis_path is None or text_text_similarity_analysis_path == '': raise Exception("asr生成的文件路径不能为空,请先完成上一步操作") similarity_dir = os.path.join(base_role_dir, params.text_similarity_output_dir) - text_text_similarity_analysis_info = f"相似度分析成功:生成目录{similarity_dir}" - open_text_similarity_analysis(text_text_similarity_analysis_path, similarity_dir) + + time_consuming, _ = time_util.time_monitor(open_text_similarity_analysis)(text_text_similarity_analysis_path, + similarity_dir) + + text_text_similarity_analysis_info = f"耗时:{time_consuming:0.1f}秒;相似度分析成功:生成目录{similarity_dir}" + + # open_text_similarity_analysis(text_text_similarity_analysis_path, similarity_dir) except Exception as e: traceback.print_exc() text_text_similarity_analysis_info = f"发生异常:{e}" @@ -293,13 +321,18 @@ def similarity_audio_output(text_work_space_dir, text_role, text_base_audio_path raise Exception("基准音频路径不能为空") if text_compare_audio_dir is None or text_compare_audio_dir == '': raise Exception("待分析的音频所在目录不能为空") - similarity_list, similarity_file, similarity_file_dir = start_similarity_analysis( - base_role_dir, text_compare_audio_dir, text_base_audio_path, True) + + time_consuming, similarity_list, similarity_file, similarity_file_dir \ + = time_util.time_monitor(start_similarity_analysis)(base_role_dir, + text_compare_audio_dir, text_base_audio_path, True) + + # similarity_list, similarity_file, similarity_file_dir = start_similarity_analysis( + # base_role_dir, text_compare_audio_dir, text_base_audio_path, True) if similarity_list is None: raise Exception("相似度分析失败") - text_similarity_audio_output_info = f'相似度分析成功:生成目录{similarity_file_dir},文件{similarity_file}' + text_similarity_audio_output_info = f'耗时:{time_consuming:0.1f}秒;相似度分析成功:生成目录{similarity_file_dir},文件{similarity_file}' except Exception as e: traceback.print_exc() @@ -320,9 +353,13 @@ def sync_ref_audio(text_work_space_dir, text_role, text_sync_ref_audio_dir, raise Exception("参考音频目录不能为空") if text_sync_inference_audio_dir is None or text_sync_inference_audio_dir == '': raise Exception("推理生成的音频目录不能为空") - delete_text_wav_num, delete_emotion_dir_num = delete_inference_with_ref.sync_ref_audio(text_sync_ref_audio_dir, - text_sync_inference_audio_dir) - text_sync_ref_audio_info = f"推理音频目录{text_sync_inference_audio_dir}下,text目录删除了{delete_text_wav_num}个参考音频,emotion目录下,删除了{delete_emotion_dir_num}个目录" + time_consuming, delete_text_wav_num, delete_emotion_dir_num \ + = time_util.time_monitor(delete_inference_with_ref.sync_ref_audio)(text_sync_ref_audio_dir, + text_sync_inference_audio_dir) + # delete_text_wav_num, delete_emotion_dir_num = delete_inference_with_ref.sync_ref_audio( + # text_sync_ref_audio_dir, text_sync_inference_audio_dir) + text_sync_ref_audio_info = (f"耗时:{time_consuming:0.1f}秒;推理音频目录{text_sync_inference_audio_dir}下," + f"text目录删除了{delete_text_wav_num}个参考音频,emotion目录下,删除了{delete_emotion_dir_num}个目录") except Exception as e: traceback.print_exc() text_sync_ref_audio_info = f"发生异常:{e}" @@ -343,10 +380,17 @@ def create_config(text_work_space_dir, text_role, text_template, text_sync_ref_a if text_sync_ref_audio_dir2 is None or text_sync_ref_audio_dir2 == '': raise Exception("参考音频目录不能为空") config_file = os.path.join(base_role_dir, f'{params.reference_audio_config_filename}.json') - text_create_config_info = f"配置生成成功:生成文件{config_file}" ref_audio_manager = common.RefAudioListManager(text_sync_ref_audio_dir2) - audio_config.generate_audio_config(base_role_dir, text_template, ref_audio_manager.get_ref_audio_list(), - config_file) + + time_consuming, _ = time_util.time_monitor(audio_config.generate_audio_config)(base_role_dir, text_template, + ref_audio_manager.get_ref_audio_list(), + config_file) + + # audio_config.generate_audio_config(base_role_dir, text_template, ref_audio_manager.get_ref_audio_list(), + # config_file) + + text_create_config_info = f"耗时:{time_consuming:0.1f}秒;配置生成成功:生成文件{config_file}" + except Exception as e: traceback.print_exc() text_create_config_info = f"发生异常:{e}" @@ -514,7 +558,8 @@ with gr.Blocks() as app: button_model_inference.click(model_inference, [text_work_space_dir, text_role, text_model_inference_voice_dir, text_url, text_text, text_ref_path, text_ref_text, text_emotion, - text_test_content], [text_model_inference_info, text_asr_audio_dir, text_sync_inference_audio_dir]) + text_test_content], + [text_model_inference_info, text_asr_audio_dir, text_sync_inference_audio_dir]) app.launch( server_port=9423, diff --git a/Ref_Audio_Selector/tool/asr/funasr_asr_multi_level_dir.py b/Ref_Audio_Selector/tool/asr/funasr_asr_multi_level_dir.py index d6e04c8..43ec04d 100644 --- a/Ref_Audio_Selector/tool/asr/funasr_asr_multi_level_dir.py +++ b/Ref_Audio_Selector/tool/asr/funasr_asr_multi_level_dir.py @@ -4,6 +4,7 @@ import argparse import os import traceback import Ref_Audio_Selector.config_param.config_params as params +from Ref_Audio_Selector.common.time_util import timeit_decorator from tqdm import tqdm from funasr import AutoModel @@ -34,30 +35,7 @@ def only_asr(input_file): return text -def execute_asr(input_folder, output_folder, model_size, language): - input_file_names = os.listdir(input_folder) - input_file_names.sort() - - output = [] - output_file_name = os.path.basename(input_folder) - - for name in tqdm(input_file_names): - try: - text = model.generate(input="%s/%s" % (input_folder, name))[0]["text"] - output.append(f"{input_folder}/{name}|{output_file_name}|{language.upper()}|{text}") - except: - print(traceback.format_exc()) - - output_folder = output_folder or "output/asr_opt" - os.makedirs(output_folder, exist_ok=True) - output_file_path = os.path.abspath(f'{output_folder}/{output_file_name}.list') - - with open(output_file_path, "w", encoding="utf-8") as f: - f.write("\n".join(output)) - print(f"ASR 任务完成->标注文件路径: {output_file_path}\n") - return output_file_path - - +@timeit_decorator def execute_asr_multi_level_dir(input_folder, output_folder, model_size, language): output = [] output_file_name = os.path.basename(input_folder) diff --git a/Ref_Audio_Selector/tool/audio_inference.py b/Ref_Audio_Selector/tool/audio_inference.py index a93328d..a5cc7e5 100644 --- a/Ref_Audio_Selector/tool/audio_inference.py +++ b/Ref_Audio_Selector/tool/audio_inference.py @@ -2,6 +2,7 @@ import os import requests import itertools import Ref_Audio_Selector.config_param.config_params as params +from Ref_Audio_Selector.common.time_util import timeit_decorator from urllib.parse import urlparse, parse_qs, urlencode, urlunparse, quote @@ -72,6 +73,7 @@ def safe_encode_query_params(original_url): return encoded_url +@timeit_decorator def generate_audio_files(url_composer, text_list, emotion_list, output_dir_path): # Ensure the output directory exists output_dir = os.path.abspath(output_dir_path) diff --git a/Ref_Audio_Selector/tool/audio_similarity.py b/Ref_Audio_Selector/tool/audio_similarity.py index afb6a52..3c6fe1c 100644 --- a/Ref_Audio_Selector/tool/audio_similarity.py +++ b/Ref_Audio_Selector/tool/audio_similarity.py @@ -6,11 +6,9 @@ import librosa def check_audio_duration(path, min_duration=3, max_duration=10): try: - # 加载音频文件 - audio, sample_rate = librosa.load(path) - # 计算音频的时长(单位:秒) - duration = librosa.get_duration(y=audio, sr=sample_rate) + # 直接计算音频文件的时长(单位:秒) + duration = librosa.get_duration(filename=path) # 判断时长是否在3s至10s之间 if min_duration <= duration <= max_duration: diff --git a/Ref_Audio_Selector/tool/speaker_verification/voice_similarity.py b/Ref_Audio_Selector/tool/speaker_verification/voice_similarity.py index 8a97d9b..e4a5ed9 100644 --- a/Ref_Audio_Selector/tool/speaker_verification/voice_similarity.py +++ b/Ref_Audio_Selector/tool/speaker_verification/voice_similarity.py @@ -1,5 +1,6 @@ import argparse import os +from Ref_Audio_Selector.common.time_util import timeit_decorator from modelscope.pipelines import pipeline @@ -10,6 +11,7 @@ sv_pipeline = pipeline( ) +@timeit_decorator def compare_audio_and_generate_report(reference_audio_path, comparison_dir_path, output_file_path): # Step 1: 获取比较音频目录下所有音频文件的路径 comparison_audio_paths = [os.path.join(comparison_dir_path, f) for f in os.listdir(comparison_dir_path) if diff --git a/Ref_Audio_Selector/tool/text_comparison/asr_text_process.py b/Ref_Audio_Selector/tool/text_comparison/asr_text_process.py index 79e2f1f..486391e 100644 --- a/Ref_Audio_Selector/tool/text_comparison/asr_text_process.py +++ b/Ref_Audio_Selector/tool/text_comparison/asr_text_process.py @@ -2,6 +2,7 @@ import os import argparse from collections import defaultdict from operator import itemgetter +from Ref_Audio_Selector.common.time_util import timeit_decorator import Ref_Audio_Selector.tool.text_comparison.text_comparison as text_comparison import Ref_Audio_Selector.config_param.config_params as params import Ref_Audio_Selector.common.common as common @@ -88,6 +89,7 @@ def format_list_to_text(data_list, output_filename): output_file.write(formatted_line) +@timeit_decorator def process(asr_file_path, output_dir, similarity_enlarge_boundary): # 检查输出目录是否存在,如果不存在则创建 if not os.path.exists(output_dir):