Utils 模块提供 lessampler 的基础设施工具类,包括分级日志系统、高性能计时器、数组转换工具和自定义异常体系。这些工具类为整个项目提供一致的基础功能支持,确保代码的健壮性和可调试性。
良好的工具类设计是软件工程质量的重要体现。Utils 模块的设计遵循以下原则:
最小依赖 :尽量使用标准库,减少外部依赖
线程安全 :支持多线程环境使用
条件编译 :通过宏开关控制调试功能
语义清晰 :类名和接口设计直观易懂
模块结构 Utils/ ├── LOG.h # 分级日志系统 ├── Timer.h # 微秒精度计时器 ├── VectorWrapper.h # 数组转向量模板 └── exception.h # 自定义异常体系
LOG.h 分级日志系统 YALL(Yet Another Logging Library)是一个分级、彩色、线程安全的日志系统。
日志级别定义 enum Yall_LEVEL { LOG_DUMP, LOG_EVAL, LOG_DEBUG, LOG_OK, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_CRITICAL, };
级别设计理念 :
级别
用途
条件编译
输出颜色
DUMP
详细数据输出
DUMP_DATA 宏
白色 + 源码位置
EVAL
性能计时输出
TIME_EVAL 宏
紫色
DEBUG
调试追踪
DEBUG_MODE 宏
白色 + 源码位置
OK
操作成功
常开
绿色
INFO
信息提示
常开
蓝色
WARN
警告信息
常开
黄色
ERROR
错误报告
常开
红色
CRITICAL
严重错误
常开
红色背景
Yall_Inst 基类 class Yall_Inst {public : explicit Yall_Inst (Yall_LEVEL logLevel) { this ->logLevel = logLevel; }; virtual void operator <<(const std::string &msg) {};protected : std::string name; Yall_LEVEL logLevel; std::mutex streamMtx; };
设计要点 :
使用 std::mutex 确保多线程环境下的输出顺序
基类定义接口,派生类实现具体行为
Yall_Instance 标准日志器 class Yall_Instance : Yall_Inst {public : explicit Yall_Instance (Yall_LEVEL logLevel) : Yall_Inst(logLevel) { }; void operator <<(const std::string &msg) override { std::lock_guard<std::mutex> lock (streamMtx) ; switch (logLevel) { case Yall_LEVEL::LOG_OK: std::cout << cc::green << "[OKAY]" << cc::reset; break ; case Yall_LEVEL::LOG_INFO: std::cout << cc::cyan << "[INFO]" << cc::reset; break ; case Yall_LEVEL::LOG_WARN: std::cout << cc::yellow << "[WARN]" << cc::reset; break ; case Yall_LEVEL::LOG_ERROR: std::cout << cc::red << "[ERRO]" << cc::reset; break ; case Yall_LEVEL::LOG_CRITICAL: std::cout << cc::on_red << "[CRIT]" << cc::reset; break ;#if TIME_EVAL case Yall_LEVEL::LOG_EVAL: std::cout << cc::magenta << "[TIME]" << cc::reset; break ;#endif default : break ; } std::cout << " " << msg << " " << std::endl; }; };
ColorCout 库集成 :
cc::green, cc::cyan, cc::yellow 等:设置文本颜色
cc::on_red:设置背景色
cc::reset:重置颜色
Yall_Debug_Instance 调试日志器 class Yall_Debug_Instance : Yall_Inst {public : explicit Yall_Debug_Instance (Yall_LEVEL logLevel) : Yall_Inst(logLevel) { enable = false ; } void SetDebugInfo (const std::string &file, const std::string &func, int line) { this ->FILE = file; this ->FUNC = func; this ->LINE = line; } void EnableDebug () { enable = true ; } void DisableDebug () { enable = false ; } void operator <<(const std::string &msg) override { std::lock_guard<std::mutex> lock (streamMtx) ; switch (logLevel) {#if DUMP_DATA case LOG_DUMP: std::cout << cc::cyan << "[FUNC] " << std::left << std::setw (23 ) << cc::reset << fmt (this ->FUNC) << " " << cc::yellow << "[FILE] " << std::setw (23 ) << cc::reset << fmt (this ->FILE) << " " << cc::green << "[LINE] " << std::setw (4 ) << cc::reset << this ->LINE << " " << cc::white << "[DUMP] " << cc::reset << msg << " " << std::endl; break ;#endif case LOG_DEBUG: std::cout << cc::cyan << "[FUNC] " << std::left << std::setw (23 ) << cc::reset << fmt (this ->FUNC) << " " << cc::yellow << "[FILE] " << std::setw (23 ) << cc::reset << fmt (this ->FILE) << " " << cc::green << "[LINE] " << std::setw (4 ) << cc::reset << this ->LINE << " " << cc::white << "[DEBUG] " << cc::reset << msg << " " << std::endl; break ; default : break ; } }private : std::string FILE = {}; std::string FUNC = {}; int LINE = {}; bool enable = false ; static std::string fmt (std::string sv) { if (sv.length () > 20 ) { return std::string ("..." ) + sv.substr (sv.length () - 20 , sv.length ()); } else { return sv; } } };
调试输出格式 :
[FUNC] ...WorldModule::F0Estim [FILE] ...orldModule/WorldModule.cpp [LINE] 123 [DEBUG] Estimating F0...
Yall 单例管理器 class Yall {public : Yall (Yall const &) = delete ; void operator =(Yall const &) = delete ; static Yall_Instance &GetYall (Yall_LEVEL logLevel) { auto it = GetInstance ().yall_inst.find (logLevel); if (it == GetInstance ().yall_inst.end ()) { auto *logger = new Yall_Instance (logLevel); GetInstance ().yall_inst[logLevel] = logger; return *logger; } return *it->second; }; static Yall_Debug_Instance &GetDebugYall (Yall_LEVEL logLevel, const std::string &FILE, const std::string &FUNC, int LINE) { auto it = GetDebugInstance ().yall_debug_inst.find (logLevel); if (it == GetDebugInstance ().yall_debug_inst.end ()) { auto *logger = new Yall_Debug_Instance (logLevel); GetDebugInstance ().yall_debug_inst[logLevel] = logger; logger->SetDebugInfo (FILE, FUNC, LINE); return *logger; } it->second->SetDebugInfo (FILE, FUNC, LINE); return *it->second; };private : std::unordered_map<Yall_LEVEL, Yall_Instance *> yall_inst; std::unordered_map<Yall_LEVEL, Yall_Debug_Instance *> yall_debug_inst; Yall () = default ; static Yall &GetInstance () { static Yall inst; return inst; }; static Yall &GetDebugInstance () { static Yall inst_d; return inst_d; }; };
单例模式特点 :
使用静态局部变量实现 Meyers 单例
unordered_map 存储各级别的日志器实例
避免每次日志调用都创建新对象
宏接口定义 #if __GNUC__ #define YALL_FUNC_ __PRETTY_FUNCTION__ #else #define YALL_FUNC_ __func__ #endif #define YALL_DUMP_ Yall::GetDebugYall(Yall_LEVEL::LOG_DUMP, __FILE__, YALL_FUNC_, __LINE__) #define YALL_DEBUG_ Yall::GetDebugYall(Yall_LEVEL::LOG_DEBUG, __FILE__, YALL_FUNC_, __LINE__) #define YALL_OK_ Yall::GetYall(Yall_LEVEL::LOG_OK) #define YALL_EVAL_ Yall::GetYall(Yall_LEVEL::LOG_EVAL) #define YALL_INFO_ Yall::GetYall(Yall_LEVEL::LOG_INFO) #define YALL_WARN_ Yall::GetYall(Yall_LEVEL::LOG_WARN) #define YALL_ERROR_ Yall::GetYall(Yall_LEVEL::LOG_ERROR) #define YALL_CRITICAL_ Yall::GetYall(Yall_LEVEL::LOG_CRITICAL)
使用示例 YALL_INFO_ << "Starting audio processing..." ; YALL_OK_ << "Model loaded successfully" ; YALL_WARN_ << "Audio file is stereo, converting to mono" ; YALL_ERROR_ << "Failed to open audio file" ; YALL_DEBUG_ << "Initializing WorldModule" ; YALL_EVAL_ << "F0 Estimation: 152ms" ; YALL_DUMP_ << "F0 array: [440.0, 441.5, 439.8, ...]" ;
Timer.h 高精度计时器 Timer 类提供微秒级别的计时功能,用于性能分析和优化。
类定义 class Timer {public : Timer () { g_start_time = get_perf_count (); start_time = g_start_time; }; void SetTimer () { end_time = 0 ; start_time = get_perf_count (); } uint64_t GetTimer () { end_time = get_perf_count (); return end_time - start_time; } std::string GetTimer (const std::string &info) { end_time = get_perf_count (); return [&]() -> std::string { auto time = end_time - start_time; if (time / 1000 == 0 ) return info + std::to_string (end_time - start_time) + "us" ; else return info + std::to_string ((end_time - start_time) / 1000 ) + "ms" ; }(); } std::string EndTimer () { g_end_time = get_perf_count (); return [&]() -> std::string { auto time = g_end_time - g_start_time; if (time / 1000 == 0 ) return std::to_string (g_end_time - g_start_time) + "us" ; else return std::to_string ((g_end_time - g_start_time) / 1000 ) + "ms" ; }(); }protected : uint64_t start_time = 0 ; uint64_t end_time = 0 ; uint64_t g_start_time = 0 ; uint64_t g_end_time = 0 ; private : static uint64_t get_perf_count () { return std::chrono::duration_cast <std::chrono::microseconds>( std::chrono::steady_clock::now ().time_since_epoch ()).count (); }; };
计时精度 使用 std::chrono::steady_clock:
单调递增 :不受系统时间调整影响
高精度 :微秒级别分辨率
跨平台 :标准 C++11 实现
使用模式 Timer timer; timer.SetTimer (); YALL_EVAL_ << timer.GetTimer ("操作1耗时: " ); timer.SetTimer (); YALL_EVAL_ << timer.GetTimer ("操作2耗时: " ); YALL_INFO_ << "总耗时: " + timer.EndTimer ();
实际应用示例 void lessampler::run () { Timer timer; timer.SetTimer (); audio_model_io.ReadAudioModel (configure); YALL_INFO_ << timer.GetTimer ("Read Audio Model: " ); timer.SetTimer (); AudioProcess audioProcess (origin_audio_model, shine_para) ; YALL_INFO_ << timer.GetTimer ("Processing Model: " ); timer.SetTimer (); Synthesis synthesis (trans_audio_model, shine_para.output_samples) ; YALL_INFO_ << timer.GetTimer ("Synthesis Audio: " ); YALL_OK_ << "All Process Done: " + timer.EndTimer (); }
VectorWrapper.h 数组转向量模板 VectorWrapper 是一个简单的模板工具,用于将 C 风格数组转换为 std::vector。
类定义 template <typename T, int N>struct VectorWrapper { explicit VectorWrapper (T (&D)[N]) { std::copy (D, D + N, std::back_inserter (v)); } std::vector<T> v; };
模板参数 :
使用示例 int arr[] = {1 , 2 , 3 , 4 , 5 };VectorWrapper<int , 5> wrapper (arr) ; std::vector<int > vec = wrapper.v; double freq_table[] = {440.0 , 466.16 , 493.88 , 523.25 };VectorWrapper<double , 4> freq_vec (freq_table) ;
应用场景 :
将硬编码的数组转换为可操作的 vector
支持迭代器和 STL 算法
类型安全的数组转换
exception.h 自定义异常体系 exception.h 定义了 lessampler 专用的异常类型,提供语义清晰的错误报告。
异常类型定义 class file_open_error : public std::runtime_error {public : explicit file_open_error (const std::string &what) : std::runtime_error("Fail to open file: " + what + "." ) { }; };class header_check_error : public std::runtime_error {public : header_check_error (const std::string &what, const std::string &expect) : std::runtime_error ("Header: " + what + " is not same as " + expect + "." ) {}; };class file_version_error : public std::runtime_error {public : explicit file_version_error (const std::string &what) : std::runtime_error(what + " Version Mismatch." ) { }; };class parameter_error : public std::runtime_error {public : explicit parameter_error (const std::string &what) : std::runtime_error("Parameter Error: " + what + "." ) { }; };class type_error : public std::runtime_error {public : explicit type_error (const std::string &what) : std::runtime_error("Type Error: " + what + "." ) { }; };class audio_file_error : public std::runtime_error {public : explicit audio_file_error (const std::string &what) : std::runtime_error("Audio File Error: " + what + "." ) { }; };
异常类型对照
异常类型
使用场景
示例消息
file_open_error
文件打开失败
“Fail to open file: input.wav.”
header_check_error
文件头验证失败
“Header: xxx is not same as shine.”
file_version_error
版本校验失败
“Please Regenerate Audio Model Version Mismatch.”
parameter_error
参数验证失败
“Parameter Error: offset exceeds audio length.”
type_error
类型转换错误
“Type Error: invalid FFT size value.”
audio_file_error
音频处理错误
“Audio File Error: unsupported format.”
异常处理模式 int main (int argc, char *argv[]) { try { lessampler lessampler (argc, argv) ; lessampler.run (); } catch (const std::runtime_error &error) { YALL_ERROR_ << error.what (); return -1 ; } return 0 ; }
使用示例 void ConfigUnit::SetConfig (const std::filesystem::path &exec_path) { if (config_file_path.empty ()) { throw file_open_error ("Configure file: " + config_file_path.string ()); } }void AudioModelIO::ReadAudioContent () { if (ver_string != _configure.get_version ()) { throw file_version_error ("Please Regenerate Audio Model" ); } }void libUTAU::CheckPara (const lessAudioModel& audioModel) { if (utauPara.offset + utauPara.last_unused_part >= utauPara.wave_length) throw parameter_error ("The audio offset and whitespace are greater than the required audio length" ); }
条件编译开关设计 日志系统通过预定义宏控制输出行为:
#ifdef DEBUG_MODE #endif #ifdef DUMP_DATA #endif #ifdef TIME_EVAL #endif
配置方式 :
通过 CMake 定义:
if (CMAKE_BUILD_TYPE MATCHES Debug) target_compile_definitions (lessampler PRIVATE DEBUG_MODE DUMP_DATA TIME_EVAL)endif ()
或通过 lessconfig.ini:
工具类设计哲学总结 LOG.h 设计要点
分级输出 :不同级别用不同颜色,便于快速识别
条件编译 :调试输出不影响发布版性能
源码追踪 :DEBUG 级别自动附加文件名、函数名、行号
线程安全 :mutex 保护多线程输出
单例模式 :避免重复创建日志器对象
Timer.h 设计要点
高精度 :微秒级别,足够测量大多数操作
单调时钟 :不受系统时间调整影响
双模式 :支持分段计时和总计时
自动格式化 :根据时长选择 μs 或 ms 单位
VectorWrapper.h 设计要点
模板推导 :自动获取数组长度,无需手动指定
类型安全 :编译时检查类型匹配
简单实用 :单一职责,易于理解和使用
exception.h 设计要点
继承标准异常 :符合 C++ 异常处理规范
语义命名 :类名直接表达错误类型
统一格式 :所有异常消息格式一致
顶层捕获 :main 函数统一捕获处理