技术说明
约 1248 字大约 4 分钟
架构总览
┌──────────────────────────────┐
│ Vue 3 + VueFluentWidgets │ Tauri 2 (Rust) 桌面外壳
│ Vite 构建的前端界面 │
└──────────────┬───────────────┘
│ 原生 Command IPC(无 HTTP 端口)
┌──────────────▼───────────────┐
│ Rust 指纹引擎(casi 工作区) │ 索引管理 / 匹配 / 导出 / 收藏 / 设置
│ casi-core 指纹核心 │ 指纹提取 / 索引加载 / 多段聚类匹配
│ casi-index 并行构建 │ .casi 二进制索引(mmap 零解析)
└──────────────┬───────────────┘
│
Rust FFT / 自研峰值选取 / 内置 FFmpeg(sidecar)- 桌面外壳:Tauri 2(Rust),Single-process:窗口 + 原生 command IPC + 本地引擎共进程。
- 引擎:Rust 工作区(casi-core / casi-index / casi-search),由 Tauri 命令直通;
casi-server提供可选的 HTTP/服务化形态。 - 前端:Vue 3 + Vite,采用 VueFluentWidgets 的 Fluent Design 组件。
- 音频解码:WAV 直读;FLAC/MP3/AAC/M4A/WMA 等由随包 FFmpeg(GPL sidecar)兜底解码。
指纹算法
预处理
- 采样率统一重采样为 11025 Hz 单声道。
- STFT:窗长 1024、hop 256(每帧约 23ms),使用 511 个频率 bin。
频谱峰值选取
对 STFT 幅度谱(dB)执行两步峰值提取:
- 2D 局部极大值:5×5 邻域内的局部极大值。
- 逐帧秩选峰:每帧保留相对帧内峰值 12dB 窗口内、且高于绝对下限 -65dB 的峰,最多 3 个。
秩选策略保证峰值集合对整体音量差异与切片时间原点不敏感——这是短样本能稳定命中完整文件的关键。
峰值对哈希
每个锚点峰与其后 25 个峰组成峰值对 (f1, f2, dt),其中:
f1、f2:两个峰的频率 bin(各 9 bit);dt:帧时间差(12 bit,上限 4095 帧)。
组合为 30-bit 哈希值,最终得到每段音频的哈希序列。
索引结构与存储
.casi 二进制索引(v26.1.0+)
单个 .casi 文件包含:
- 128B 头(魔数
CASI/ 版本 / 段偏移表); 2^16桶目录(54 位哈希前 16 位)与按(hash, 时间帧)有序的 posting 数组(每行 12 字节h/fid/t);- 文件元数据表与字符串池。
fid 全局唯一(转换为旧库时沿用 (分片编号<<24)|行号),跨分片不冲突。
加载零解析:直接 mmap 映射,桶目录 + 桶内二分查找;冷启动不触碰未用页,12GB 级索引打开毫秒级。
分段与元数据
- 按目标段加载内存(MB)把文件列表分组:以估算哈希量(时长 × 经验速率)为代价,贪心分组到预算内。
- 索引目录下的
index_meta.json记录首次构建时的segment_size_mb;index.json记录版本与文件清单(增量构建用)。 - 旧版或外部导入索引没有元数据时,
segment_size_mb视为0(单段)。
内存缓存
- 引擎维护段级 LRU 缓存(默认最多 4 段),命中即返回,避免重复加载。
- 匹配对已加载的 CasiFile 只读(mmap),可并发执行;加载过程加锁串行化,避免并发重复加载造成内存峰值。
- 设置中的「检索后卸载索引」开启后,每次匹配结束会释放本次使用的段;卸载带实例校验。
匹配流程
- 按索引名与分段名解析段目录,从缓存加载(或重新加载)
.casi。 - 对样本做相同预处理与指纹提取(支持起止时间裁剪)。
- 对每个候选文件执行时间偏移对齐投票:同一偏移上的峰值对哈希匹配数量最多者胜出。
- 同一文件在不同时间多次出现会被聚成多个独立命中(支持混剪、拼接场景)。
- 置信度 = 对齐哈希数 / 样本哈希数,上限 100%;阈值按样本时长自适应。
构建流程
iter_library_files按扩展名收集音频文件(支持常见格式与混合目录),可选递归。process_files按 worker 并行解码、提取哈希、写入分片,逐文件提交进度事件。- 增量构建先读取
index.json清单中的已索引路径集合,过滤出未入库的新文件再处理。 - 完成后统计
.casi头中的行数作为索引总量。
导出(全文件复制)
按匹配结果,将命中的源文件完整复制到用户选择的目录(原名,冲突自动加序号);不再做片段拼接。
音频流播放
前端播放器直接经 Tauri asset protocol 加载完整源文件(convertFileSrc),并在元数据就绪后 seek 到命中偏移,进度条显示完整文件时长。
目录结构
crates/ Rust 工作区
casi-core 指纹核心:DSP / 峰值选取 / 哈希 / 解码
casi-index .casi 索引格式:mmap 加载 / 桶目录 / 外排构建
casi-search 匹配聚类(时间偏移投票)
casi-server 服务化形态(axum)与纯函数逻辑层 api.rs
src/ Vue 3 前端
stores/ 跨页面状态与共享缓存
views/ 检索、构建、管理、收藏、设置、关于
src-tauri/ Tauri 2 (Rust):窗口 / 原生 command IPC / commands.rs