cmake_minimum_required(VERSION 3.24) project(live-caption VERSION 0.1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_AUTOMOC ON) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE) endif() option(LC_BUILD_TESTS "构建单元测试" ON) # --------------------------------------------------------------------------- # llama.cpp / libmtmd 的搜索路径 # # Debian/Deepin 把 llama 的 CMake config 放在非标准目录 cmake-private/, # 而 pkg-config 的 llama.pc 又漏了 -lmtmd。所以这里把两个路径都塞进搜索前缀, # 并单独用 find_library 找 mtmd。 # --------------------------------------------------------------------------- foreach(_p /usr/lib/x86_64-linux-gnu/llama /usr/lib/x86_64-linux-gnu/llama/cmake-private /usr/lib/x86_64-linux-gnu/llama/pkgconfig) if(EXISTS "${_p}") list(APPEND CMAKE_PREFIX_PATH "${_p}") set(ENV{PKG_CONFIG_PATH} "${_p}:$ENV{PKG_CONFIG_PATH}") endif() endforeach() find_package(Qt6 REQUIRED COMPONENTS Core Widgets Network) find_package(PkgConfig REQUIRED) find_package(Threads REQUIRED) pkg_check_modules(SAMPLERATE REQUIRED IMPORTED_TARGET samplerate) pkg_check_modules(PULSE REQUIRED IMPORTED_TARGET libpulse) # --- llama:优先系统 pkg-config,其次 CMake config,最后 FetchContent 兜底 --- pkg_check_modules(LLAMA QUIET IMPORTED_TARGET llama) if(LLAMA_FOUND) set(LC_LLAMA_TARGET PkgConfig::LLAMA) message(STATUS "llama: 使用系统 pkg-config (${LLAMA_VERSION})") else() message(STATUS "llama: 系统未找到,改用 FetchContent 拉取固定版本") include(FetchContent) set(FETCHCONTENT_TRY_FIND_PACKAGE_MODE ALWAYS) # 先 find_package,失败再拉源码 FetchContent_Declare(llama GIT_REPOSITORY https://github.com/ggml-org/llama.cpp.git GIT_TAG b9951 FIND_PACKAGE_ARGS NAMES llama CONFIG) set(LLAMA_BUILD_TESTS OFF CACHE BOOL "" FORCE) set(LLAMA_BUILD_SERVER OFF CACHE BOOL "" FORCE) set(LLAMA_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) FetchContent_MakeAvailable(llama) set(LC_LLAMA_TARGET llama) endif() # --- mtmd:llama.pc 里没有,单独找 --- find_library(LC_MTMD_LIB NAMES mtmd HINTS /usr/lib/x86_64-linux-gnu/llama) if(NOT LC_MTMD_LIB) message(FATAL_ERROR "找不到 libmtmd(应随 libllama0 一起安装)") endif() message(STATUS "mtmd: ${LC_MTMD_LIB}") # --------------------------------------------------------------------------- # sherpa-onnx(X-ASR / zipformer / Nemotron 等流式 ONNX 模型) # # 注意:官方预编译库是用旧 C++ ABI 编译的,所以只能用它的 C API。 # 安装:把官方的 sherpa-onnx-vX.Y.Z-linux-x64-shared.tar.bz2 解到某个目录, # 用 -DSHERPA_ONNX_ROOT= 指定(默认见下)。 # --------------------------------------------------------------------------- set(SHERPA_ONNX_ROOT "$ENV{HOME}/.local/opt/sherpa-onnx" CACHE PATH "sherpa-onnx 安装目录") set(LC_HAS_SHERPA OFF) if(EXISTS "${SHERPA_ONNX_ROOT}/include/sherpa-onnx/c-api/c-api.h") find_library(LC_SHERPA_C_API NAMES sherpa-onnx-c-api HINTS "${SHERPA_ONNX_ROOT}/lib" REQUIRED) find_library(LC_SHERPA_ORT NAMES onnxruntime HINTS "${SHERPA_ONNX_ROOT}/lib" REQUIRED) set(LC_HAS_SHERPA ON) message(STATUS "sherpa-onnx: ${SHERPA_ONNX_ROOT}") else() message(WARNING "未找到 sherpa-onnx(${SHERPA_ONNX_ROOT}),流式 ONNX 后端将不可用") endif() # --------------------------------------------------------------------------- # core:全部业务逻辑,刻意不依赖 Qt(方便测试与复用) # --------------------------------------------------------------------------- add_library(livecaption_core STATIC src/core/transcript.cpp src/core/audio.cpp src/core/qwen3asr.cpp src/core/streamingasr.cpp src/core/livepipeline.cpp ) if(LC_HAS_SHERPA) target_sources(livecaption_core PRIVATE src/core/sherpaasr.cpp) target_compile_definitions(livecaption_core PUBLIC LC_HAVE_SHERPA=1) target_include_directories(livecaption_core PRIVATE "${SHERPA_ONNX_ROOT}/include") target_link_libraries(livecaption_core PUBLIC ${LC_SHERPA_C_API} ${LC_SHERPA_ORT}) endif() target_include_directories(livecaption_core PUBLIC src/core) target_link_libraries(livecaption_core PUBLIC Threads::Threads PkgConfig::SAMPLERATE PkgConfig::PULSE ${LC_LLAMA_TARGET} ${LC_MTMD_LIB} ) # --------------------------------------------------------------------------- # models:模型注册表与存储(Qt;注册表编译进二进制) # --------------------------------------------------------------------------- add_library(livecaption_models STATIC src/models/modelstore.cpp src/models/modeldownloader.cpp ) target_include_directories(livecaption_models PUBLIC src/models) target_link_libraries(livecaption_models PUBLIC Qt6::Core Qt6::Network) qt_add_resources(livecaption_models "registry" PREFIX "/" BASE "${CMAKE_CURRENT_SOURCE_DIR}" FILES data/models.json) # --------------------------------------------------------------------------- # ui:悬浮窗与 Qt 适配层 # --------------------------------------------------------------------------- add_library(livecaption_ui STATIC src/ui/captionwindow.cpp src/ui/livetranscriber.cpp src/ui/appsettings.cpp src/ui/settingsdialog.cpp ) target_include_directories(livecaption_ui PUBLIC src/ui) target_link_libraries(livecaption_ui PUBLIC livecaption_core livecaption_models Qt6::Widgets) # --------------------------------------------------------------------------- # 可执行程序 # --------------------------------------------------------------------------- # GUI:悬浮字幕窗 add_executable(live-caption apps/gui/main.cpp) target_include_directories(live-caption PRIVATE apps/common) target_link_libraries(live-caption PRIVATE livecaption_ui livecaption_models) # CLI:文件 / 流式文件 / 命令行实时;不链接 Qt Widgets add_executable(live-caption-cli apps/cli/main.cpp) target_include_directories(live-caption-cli PRIVATE apps/common) target_link_libraries(live-caption-cli PRIVATE livecaption_core livecaption_models) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") foreach(_t livecaption_core livecaption_ui livecaption_models live-caption live-caption-cli) target_compile_options(${_t} PRIVATE -Wall -Wextra -Wno-unused-parameter) endforeach() endif() # --------------------------------------------------------------------------- # 安装后的 RPATH # # 1) $ORIGIN/../<私有库目录>:打包时随包携带的 sherpa-onnx 运行时(lib/ 下的相对路径); # 2) llama / mtmd:发行版把这两个库放在 .../llama 子目录,而该目录不在 ld 的默认搜索 # 路径里(ldconfig -p 里看不到),所以必须显式写进 RPATH,否则安装后起不来。 # --------------------------------------------------------------------------- set(LC_PRIVATE_LIBDIR "lib/live-caption") set(_lc_install_rpath "\$ORIGIN/../${LC_PRIVATE_LIBDIR}") get_filename_component(_lc_llama_dir "${LC_MTMD_LIB}" DIRECTORY) if(IS_DIRECTORY "${_lc_llama_dir}") list(APPEND _lc_install_rpath "${_lc_llama_dir}") endif() set_target_properties(live-caption live-caption-cli PROPERTIES INSTALL_RPATH "${_lc_install_rpath}" INSTALL_RPATH_USE_LINK_PATH OFF) message(STATUS "安装后 RPATH: ${_lc_install_rpath}") # --------------------------------------------------------------------------- if(LC_BUILD_TESTS) enable_testing() add_subdirectory(tests) endif() # --------------------------------------------------------------------------- # 安装与打包(CPack → .deb) # --------------------------------------------------------------------------- include(cmake/Packaging.cmake)