关于 Cochlea
Cochlea 是面向 Agent 和音频开发者的离线音频引擎,可用于创作、渲染、检查和验证音频。它将音频转换为精简报告和频谱图,并支持确定性渲染及常见格式间的特征比较。
主要功能
- 01用 RON 数据编写乐谱
- 02离线确定性音频渲染
- 03分析响度、音高、速度与节奏
- 04生成频谱图和特征报告
- 05按感知特征比较音频
- 06导入和导出 MIDI
适用场景
- 01依据内嵌断言验证渲染结果
- 02检查长音频文件中的指定片段
- 03比较重新渲染后的音乐变化
- 04通过 JSON 报告或频谱图检查音频
MCP 相关
stdio MCP 服务提供 12 个工具,支持渲染、探测、频谱图、乐谱检查、音频比较、MIDI 转换、转录和乐谱参考。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
cochlea

面向 Agent 的无头音频引擎。 将乐谱编写为数据,离线渲染为确定性的 PCM,然后用数字聆听——响度、起音、音高、调性、频谱图——并断言听到的内容。创作 → 渲染 → 探测 → 验证,全程无需人耳(也无需音频设备)。

Agent 看到的内容:渲染并探测后,examples/scores/first_light.ron 的梅尔频谱图——该乐谱用于下方的示例。看不到 PCM。
use cochlea_score::*;
let score = Score::new(SampleRate(48_000), Ppq(960))
.time_signature(4, 4)
.tempo(Ticks(0), Bpm(120.0))
.track("lead", Instrument::preset("saw_lead"))
.note("lead", bar(1).beat(1), Dur::quarter(), Pitch::A4, Vel(96))
.automate("lead", Param::CUTOFF_HZ,
keys![(bar(1), 400.0, ease_in_out()), (bar(3), 4_000.0)]);
let rendered = cochlea_render::render(&score)?;
rendered.write_wav("mix.wav")?;
use cochlea_verify::{VerifyExt, Tol, Ms, Cents, Db};
let report = rendered.verify(&score)
.true_peak_below(-1.0)
.pitch_matches_score("lead", Cents(10.0))
.monotone("lead", Param::CUTOFF_HZ, bar(1)..bar(3))
.silent_after(bar(5))
.run();
assert!(report.passed);
也可以完全通过命令行,以 RON 编写乐谱:
cochlea render score.ron --out mix.wav --stems stems/ --verify
cochlea probe input.wav --json report.json --spectro spec.png
cochlea probe input.wav --digest --window-ms 500
cochlea probe input.mp3 --from 42.0 --to 44.5 # zoom into a window, any format
cochlea diff a.wav b.wav --tier2 --spectro delta.png
cochlea lint score.ron
cochlea spectro input.wav --out spec.png --annotate # draw beats/onsets/pitch on the image
cochlea import song.mid --out score.ron # SMF -> score, timing exact
cochlea transcribe solo.wav --out score.ron # audio -> score, the inverse of render
cochlea reference # the full score-authoring reference, generated from the live preset bank
cochlea probe 可处理任意 WAV,此外还支持 FLAC(解码结果逐位精确)、mp3 和 ogg——仍无需 ffmpeg,也无需乐谱。这就是入口:将未由你渲染的音频交给它,即可获得同样的 JSON 报告和频谱图,供 Agent 检查自己的作品。
Agent 如何聆听
编写 → 渲染 → 探测(JSON)→ 频谱图(一次视觉调用)→ 验证
- 编写乐谱数据(RON,或上文的 Rust 构建器)。
- 渲染为确定性的 PCM ——
cochlea render score.ron --out mix.wav。 - 探测混音并生成精简的 JSON 报告(响度、起音、音高、调性、静音、削波)——
cochlea probe mix.wav --json report.json。 不需要图像或音频:Agent 读取数字即可。 - 查看,当数字不足以说明问题时——
cochlea spectro mix.wav --out spec.png会渲染一张小型 PNG,供 Agent 通过一次视觉调用查看,无需对原始采样进行推理。 - 验证——
cochlea render score.ron --verify会运行乐谱中嵌入的断言,失败时以非零状态码退出。Agent 可以自行重试,无需人工确认“听起来没问题”。
当长文件中间某处需要仔细聆听时,每个读取工具都支持 --from/--to。只探测第 17–19 小节,或只绘制副歌部分的频谱图。截取位置精确到帧,报告时间相对于截取部分,source.start_ms 则记录其原始位置。这样,整套工具就成了变焦镜头,而不再只能报告整个文件。
这里的重点是成本,而非事后才考虑这一点。上面的 first_light 渲染包含 7 秒、48 kHz、32 位浮点 PCM,大小为 2.7 MB。相同设置下,一首 3 分钟的乐曲约为 66 MB。你不会把这些数据作为文本交给 Agent,而逐个采样读取则更糟。
它的探测报告则只有几 KB 的 JSON。以下是经过精简、仅保留关键字段的 schema v5。注意 pitch.melody:乐曲的低音线和旋律会以音符事件的形式读回,这就是编写循环中的读回环节。
{
"schema_version": 5,
"source": { "sample_rate": 48000, "channels": 2, "duration_ms": 7035.708333333333, "start_ms": 0.0 },
"loudness": { "integrated_lufs": -22.700454879284784, "true_peak_dbtp": -15.910817022082783, "lra": 10.607660373688798 },
"onsets": { "count": 6, "times_ms": [1077.33, 2149.33, 2346.67, 3221.33, 4538.67, 5034.67] },
"pitch": { "voiced_ratio": 0.9847560975609756, "median_f0_hz": 110.00194603797897,
"melody": [ { "name": "A2", "start_ms": 0.0, "end_ms": 1045.3, "cents_off": 0.1 },
{ "name": "E2", "start_ms": 1077.3, "end_ms": 2112.0, "cents_off": 0.3 },
{ "name": "F#2", "start_ms": 2154.7, "end_ms": 3178.7, "cents_off": 0.2 },
{ "name": "E2", "start_ms": 3210.7, "end_ms": 4384.0, "cents_off": 0.3 },
{ "name": "E5", "start_ms": 4394.7, "end_ms": 5813.3, "cents_off": -0.4 } ] },
"timbre": { "mfcc_mean": [-37.64, 14.47, -4.44, 1.24, "..."], "mfcc_std": ["..."], "frames": 656 },
"key": { "tonic": "E", "mode": "major", "confidence": 0.8093960265638273 },
"tempo": { "bpm": 55.97014925373134, "confidence": 0.6633739386089712, "stability": 0.3333333333333333,
"candidates": [ { "bpm": 55.97014925373134, "salience": 0.6633739386089712 },
{ "bpm": 112.5, "salience": 0.21588204941945222 } ] },
"rhythm": { "grid_alignment": 0.8333333333333334, "grid": "straight", "offbeat_ratio": 0.4, "clear_rhythm": true },
"stereo": { "width": 0.02967719705208343, "correlation": 0.9981362354107913, "balance": -0.0016380539212361243 },
"structure": { "section_count": 1, "confidence": 0.0 },
"silence": { "trailing_ms": 2485.708333333333 },
"clipping": { "clipped_samples": 0, "true_peak_over_0dbtp": false }
}
频谱图则是一张小型图像。下面是 title_cue 演示——一个垫底音色,其 cutoff_hz 自动化参数在第 1–3 小节间从 250 Hz 扫到 5000 Hz:

随着扫描进行,画面顶部的暗色频带逐渐变窄,让更多高频能量随时间通过。Agent 可以直接从图像中读出这一点。演示中的 Monotone(track: "pad", param: "cutoff_hz", ...) 断言会以数值方式检查同一现象。
无论乐曲多长,都可以通过 --sheet 将频谱图平铺成一张联系表,而不是生成一条长带。这里通过 --bars-per-tile 2 设置每个平铺块包含两个小节:

无需上下文窗口即可读取音频
probe --digest 会跳过 JSON,改为输出确定性的文本摘要:每个特征维度占一行,随后是一个窗口化时间线,最多约 40 行。以下是 drum_groove 演示的实际输出——时长 20.8 秒,包含四条音轨,节奏、立体声和结构维度都能在一个屏幕内看到:
cochlea digest: 20.755s 2ch 48000Hz
loudness: integrated=-24.06 momentary_max=-22.42 true_peak=-5.95 lra=1.61
key: A# minor (conf 0.54) pitch: voiced=23% median=63.8Hz (C2 -42.8c)
melody: 6 notes C2 C2 C2 C2 A1 A1
tempo: 110.3bpm (conf 0.79, stability 1.00) alts: 54.9bpm(0.89), 36.6bpm(0.79)
rhythm: clear=true grid_align=0.98 (straight) offbeat=0.56
stereo: width=0.07 corr=0.99 bal=-0.01
structure: 1 section
onsets: count=58 rate=2.79/s
silence: leading=0ms trailing=2545ms
clipping: clipped=0 over_0dbtp=false
timeline: window=1000ms bucket=1x rows=21
idx t(s) rms peak ons f0 flags
0 0.000-1.000 -25.55 -7.36 4 64.0 -
1 1.000-2.000 -25.61 -8.37 3 63.4 -
...
速度和节奏被报告为独立的轴,因为它们的变化彼此独立。鼓独奏可以保持脉搏稳定如磐,同时鼓型却变得难以辨认,而这种差异正是 Agent 需要看到的。
这里的速度为 110.3 BPM,与创作时设定的 110 BPM 相符;stability 1.00 表示整首曲子的速度始终不变。alts 列表列出了实际存在的半速读数 54.9 BPM——它实际上是更强的原始峰值,而八度先验打破了平局,让节拍读数胜出。这样的节拍歧义是 Agent 可以权衡的数据,而不是藏在检测器内部的掷硬币。
接着,rhythm 行说明击打与该脉搏的关系:98% 的起音落在节拍细分网格上,56% 落在弱拍细分上,因此 clear=true。这是一个八分音符踩镲律动,其切分音程度以数字呈现。
在 0.2.0 之前的指标下,这个相同的律动会被判定为 clear_rhythm=false,置信度为 0.01。将踩镲、底鼓、军鼓和铺底音色叠加在三个节拍层级上,会稀释各个延迟在质量分数中的占比。基于网格的规则问的是正确的问题。
(straight) 标签是网格假设检验:分别依据直拍十六分音符和八分三连音测量对齐情况,报告会采用击打更多落在其上的那种网格。Shuffle 或 Swing 演奏会显示为 grid_align=1.00 (triplet)——识别为对齐的三连音节奏,而不是被硬套到十六分音符上并判为不整齐。
cochlea diff 会在特征空间中比较两个文件,而不是逐字节比较——问的是“我的改动是否实现了预期效果”,而不是“文件的位级内容是否相同”。以下是对 first_light.wav 和 title_cue.wav 进行差异比较的实际输出:
verdict: different (duration, loudness, onsets, key)
duration a->b +1264.3 ms
loudness integrated -5.95 LU true_peak +5.70 dB lra -8.88 LU
onsets matched=0 mean_offset=- max_offset=- unmatched_a=6 unmatched_b=5
pitch delta +0.5 cents
key a=E major (conf 0.81) b=A minor (conf 0.86) changed=true
segments max_abs_rms_delta 120.99 dB at idx=7
tempo bpm -24.01 bpm stability -0.33
rhythm clear_rhythm_changed=false grid_align -0.03 grid_changed=true
timbre mfcc_distance 4.00
stereo width +0.14 correlation -0.08 balance -0.01
structure section_count +0
timbre 行是 MFCC 频谱形状距离,不包括 c0,因为它只表示响度。使用同一乐器重新渲染时,测量值接近 0;在响度匹配的情况下,将正弦波换成锯齿波时,测量值则远高于 0。这是“重新渲染是否保留了乐器特性”的维度,而响度和音高无法体现这一点。
添加 --spectro delta.png 后,差异比较还会渲染一张带符号的差异热图:B 更响的地方显示为红色,更轻的地方显示为蓝色,未变化的地方显示为黑色。起音位置移动会呈现为一对蓝色/红色竖线,渐强扫音则会显示为红色楔形。变化会成为可见的结构,而不只是一个数字。
将渲染结果与自身或同一乐谱的重新渲染结果进行比较,判定结果会显示为 byte-identical,而不是其他结果——这是从外部检查上述确定性约定。--tier2 会将该判定变成一道关卡:如果结果逐字节相同或符合 Tier-2 等效条件,则退出码为 0;否则为 1。因此,CI 作业或 Agent 无需读取原始采样,就能捕捉回归。
作为 MCP 客户端的 Agent
cochlea-mcp 是一个基于 CLI 所用相同库构建的 stdio MCP 服务。它提供十二个工具 — render_score、probe_audio、spectrogram、lint_score、probe_digest、loudness_timeline、beat_grid、audio_diff、import_midi、export_midi、transcribe_audio 和 score_reference — 每个工具都是对相应库调用的轻量封装。任何 MCP 客户端都能通过工具调用完成相同的 compose → render → probe → spectrogram → verify 循环,而无需通过 Shell 启动子进程。
cargo install cochlea-mcp
claude mcp add cochlea -- cochlea-mcp
它之所以原生适用于 Agent,而非披着 CLI 外衣:
- 它会自我说明。
score_reference会返回完整的创作参考 — RON 语法、包含所有可自动化参数的实时预设目录(由用于验证乐谱的同一注册表生成,因此不会过时)、所有verify:断言,以及测试套件本身会渲染的完整示例。刚接入的 Agent 无需查看此仓库,也能进行创作。 - 它直接展示,而非只指出位置。
spectrogram会将图像以内嵌 MCP 图像内容的形式返回(base64 PNG,大小受限),因此即使客户端无法访问文件系统,也能通过一次视觉调用完成审阅;out_path为可选项。annotate: true会在图像上绘制检测到的节拍、起音和音高,audio_diff也可以用相同方式返回带正负值的差异热图。 - 它支持缩放查看。
probe_audio和spectrogram支持from_s/to_s— 可以像人类重听一个小节那样,专注分析长文件的 42.0–44.5 s,而无需每次都分析整个文件。 - 它可以限制访问范围。
cochlea-mcp --root DIR会在触及文件系统前,拒绝任何解析后(按规范路径解析 — 包括符号链接和..)指向DIR之外的读写操作。
完整的工具架构、参数和 JSON-RPC 帧格式见
docs/mcp.md。
安装
这九个 crate 均已发布到 crates.io:
cargo install cochlea # the CLI: render / probe / diff / lint / spectro / reference
cargo install cochlea-mcp # the MCP stdio server
cargo add cochlea-features # or any crate as a library dependency
也可以从源码安装:git clone https://github.com/richer-richard/cochlea && cd cochlea && cargo install --path crates/cli。
概念
乐谱 IR(cochlea-score)。乐谱是纯数据:音轨、音符、逐参数自动化、阶梯式变化的速度图,以及可选的总线段。它会序列化为 RON(version: 1),并通过测试验证双向往返。
位置的写法符合口语习惯 — bar(3).beat(2) — 时值则是精确分数:Dur::quarter()、"3/16",并支持附点和三连音简写。未落在 tick 网格上的位置会报错,而不会悄悄取整。cochlea import 会读取标准 MIDI 文件,并保留时序:SMF tick 会原样落在网格上,GM 音色会转换为带标签的预设猜测。
整数时间是事实标准。 所有时间都以 tick 表示,分辨率为 960 PPQ。BPM 会在最初一次性转换为每个四分音符对应的整数纳秒数。将 tick 转换为采样点时,使用精确的有理数 u64/u128 运算(fenestra-anim 的 mul_div),并在调度事件时应用一次。不会累积浮点秒数,不会读取实时时钟,且一项属性测试验证了在 10⁹ 个 tick 内不会产生漂移。
合成器(cochlea-synth)。基于 fundsp 构建的十一种预设。其中八种采用减法合成:sine、saw_lead、square_bass、chord_pad(真正的立体声 — 其失谐锯齿波会分别向左右声道声像定位)、noise_hat、pluck、kick 和 snare。另外三种则不采用减法合成:fm_bell(带可自动化 brightness 参数的谐波 FM)、marimba(模态敲击音条)和 organ(加法拉杆风琴)。此外还有一个 reverb 插入效果。
每种乐器都会声明其可自动化参数的名称、单位、范围和默认值。乐谱会依据该注册表进行验证,同一注册表也用于生成创作参考,因此文档不会与代码脱节。所有噪声都来自一个以 (seed, sample_index) 为键的计数器式 RNG — 支持随机访问,且任何地方都没有有状态生成器。
渲染器(cochlea-render)。音频以 64 个采样为一块进行渲染,
并在事件边界处分块,因此音符时序精确到采样,而自动化以控制速率运行(在 48 kHz 下约为 1.3 ms)。各轨道独立渲染,既是并行处理的单位,也能直接生成分轨。语音分配和最早音符回收都是调度的纯函数。
主总线按固定轨道顺序以 f64 汇总分轨,然后应用乐谱中可选的主控阶段:输出增益,以及一个砖墙式前瞻限制器,其采样峰值上限可精确保持(离线处理时,前瞻只需取一个向前窗口的最大值——无需延迟线)。这是在保留 TruePeakBelow 余量的同时达到 LUFS 目标的工具。若没有主控部分,混音与分轨之和逐字节相同,这既是定义如此,也经过测试验证。
功能(cochlea-features)。一份带架构版本的 JSON 报告,涵盖:
- 响度——综合 LUFS、最大瞬时响度、真峰值和响度范围(LRA),通过 ebur128 计算;
- 基于频谱通量的起音,以及带音分偏差的 YIN 音高;
- 量化后的旋律:Agent 可将音符事件与其编写的内容进行差异比较;
- MFCC 音色摘要,以及色度和 Krumhansl-Schmuckler 调性;
- 将速度和节奏作为两个独立维度——速度给出脉冲清晰度、八度替代方案和分窗口稳定性;节奏则给出网格对齐情况(含直拍与三连音假设检验)、弱拍比例,以及经校准的
clear_rhythm; - 立体声宽度、相关性和平衡;
- 通过 Foote 新颖度检测结构边界,以及静音、尾音和削波。
此外还提供:分窗口的片段时间线、适合大模型处理的文本摘要、两个文件之间的特征空间差异,以及由所有 --from/--to 参数使用的帧精确窗口处理(Audio::window)。
频谱图(cochlea-spectro)。生成 Mel 频谱图 PNG,采用 HTK 滤波器组、viridis 配色、时间标尺和小节标记。它可以在图像上绘制分析叠加层(节拍网格、起音、音高),渲染带正负号的 A→B 差异热图,并将整首曲子平铺为一张联系表,让 Agent 通过一次视觉调用即可审阅。
验证(cochlea-verify)。即上文所示的断言 DSL。同样的断言可以嵌入乐谱 RON 的 verify: 部分;运行 cochlea render score.ron --verify 会执行这些断言,若失败则以机器可读的 JSON 报告退出并返回非零状态码。
严格限定范围的确定性
音频处理是折叠运算,而非映射:滤波器和延迟线会携带状态,因此逐采样纯度并非契约。契约分为三个层级:
| 层级 | 声明 | 适用范围 |
|---|---|---|
| 1 | 对相同输入生成逐字节相同的 PCM | 固定的 CI 目标平台(x86_64-linux,固定工具链);每个平台都测试同机重复性 |
| 2 | 跨平台的特征容差 | 综合 LUFS ±0.1 LU、起音 ±2 ms、音高 ±5 音分 |
| 3 | 频谱图哨兵测试 | 带逐像素容差的图像差异比较 |
第 1 层通过一组特定选择实现:
- DSP 路径中的所有超越函数都使用
libm。 标准库浮点方法由 clippy 配置禁止,而非仅凭惯例约束。 - 不使用快速数学,也不隐式执行 FMA。
mul_add同样被禁止。 - 所有地方都保留非规格化数。 清零处理是实时性能优化技巧,而且 x86 和 aarch64 甚至无法以统一方式实现。我们进行离线渲染,并接受偶尔出现的慢速尾音。
- 固定求和顺序,并使用 f64 主总线。
- 逐采样推进语音。 已证明 fundsp 的 SIMD 分块路径与标量路径存在偏差,因此禁止使用。
- 分析 FFT 使用
FftPlannerScalar,不进行运行时 CPU 分派。
完整的审计记录——按 fundsp 节点类别、ebur128 内部实现和 rustfft 分派情况逐项列出——见
docs/determinism.md。
功能准确性(综合基准真值,48 kHz)
| 功能 | 测试样本 | 测量结果 |
|---|---|---|
| 音高(YIN) | 440 Hz 正弦波 | 440.017 Hz — 与 A4 相差 0.07 音分 |
| 起音 | 点击音轨,0.5 s 网格 | 偏差 ≤ 4 ms(帧中心约定,256 个采样点的 hop) |
| 调性 | C 大三和弦 | C 大调,置信度 0.79 |
| 调性 | I–IV–V–I 铺底和弦进行(演示) | C 大调 |
| 响度 | −18 dBFS 峰值、997 Hz 正弦波 | −21.0 LUFS(≈ 正弦波波峰因数 −3 LU — 这是物理特性,不是误差) |
| 静音/尾音 | 1 s 音调 + 1 s 静音 | 尾部静音 960 ms,最后的可听信号位于一个 RMS 窗口之内 |
| 削波 | 驱动方波,限幅 | 已计数;已标记真峰值超出 0 |
| 速度 | 120/90 BPM 点击音轨 | ±1 BPM,脉冲清晰度 0.96,clear_rhythm=true |
| 速度 | drum_groove 演示(110 BPM 律动) | 110.29 BPM(Δ 0.01),脉冲清晰度 0.79,稳定性 1.0;55 BPM 的半速会作为候选(显著度 0.89)呈现,而不是被隐藏成一次随意猜测 |
| 节奏 | 四分音符点击 vs 均匀八分音符 | 两者的网格对齐度均为 1.0;弱拍比例分别为 0.0 和 0.49 — 切分节奏可量化 |
| 节奏鲁棒性 | 点击音轨,人为时序抖动 ±5/±10 ms | BPM 精确,对齐度 1.0,clear_rhythm 保持成立(清晰度 0.77 / 0.51) |
| 节奏鲁棒性 | 点击音轨,抖动 ±20/±30 ms | BPM 折半至半速(节拍变得模糊,使两拍延迟与一拍延迟同样清晰)— 但对齐度仍为 1.0,clear_rhythm 仍成立 |
| 节奏鲁棒性 | 22 次击打中漏掉一次 + 多出一次 | BPM 和 clear_rhythm 均不受影响 |
| 节奏误报防护 | 均匀随机的起音时间 | 对齐度 0.57(低于清晰节奏阈值 0.7),脉冲清晰度 0.10 — 两道独立门槛均未通过 |
| 速度与节奏 | 速度不变时改变模式(四分音符 → 密集八分音符) | 稳定性保持在 ≥ 0.75 — 鼓独奏的情况:变化的是节奏,不是速度 |
| 速度与节奏 | 实际速度变化(缓冲区中段从 100 → 140 BPM) | 稳定性降至 ≤ 0.75 — 用于区分两者的指标 |
| 摇摆节奏 | Shuffle(拍点 + 位于 2/3 拍处的反拍) | grid: triplet,对齐度 1.0,clear_rhythm 保持成立 — 能识别出来,不会被判为不规整 |
| 旋律 | 三个编写好的音调(A4 C5 E5) | 回读为 A4 C5 E5,起始时间偏差在 60 ms 内,中心音高偏差在 5 音分内 |
| 音色 | 同一音符、同一音量的正弦波与锯齿波 | MFCC 距离能明确区分;相同输入的测量值精确为 0 |
| 有损解码 | 同一音调分别使用 WAV、mp3 和 ogg | 不同编解码格式间的音高差在 5 音分内 |
| 结构 | 两个 8 s 片段,音色各不相同 | 边界与实际 8.0 s 切点相差不超过 1.5 s |
| 结构 | 三个 8 s 片段(A/B/A) | 边界与实际 8.0 s 和 16.0 s 切点相差均不超过 1.5 s |
设计上不依赖 ffmpeg
cochlea 使用 hound 和 symphonia 读取 WAV、FLAC、mp3 和
ogg/vorbis,两者均为纯 Rust。它会写入普通 WAV,并使用 CPU 渲染
PNG,所用组件包括 rustfft、手工编写的 mel 滤波器组、viridis LUT 和
image。
整个流程不会调用子进程、不使用系统编解码器、GPU 或音频设备。完整处理流程是纯 Rust 依赖关系图,CI 会阻止 GUI、GPU 和设备 crate 进入 Cargo.lock(deny.toml)。
解码有两种不同的保证,值得明确区分。WAV 和 FLAC 解码逐位精确 — FLAC 按规范属于无损格式,而且项目内会通过 WAV 对照文件进行验证。mp3 和 ogg 仅用作分析输入:对于给定构建结果具有可重复性,但有损编解码器已丢弃原始采样数据,因此无法再保证精确还原。
断言手册
use cochlea_verify::{VerifyExt, Tol, Ms, Cents, Db};
rendered.verify(&score)
// Mix-level loudness and headroom:
.integrated_lufs(-14.0, Tol(0.5)) // streaming-loudness target
.true_peak_below(-1.0) // intersample-safe headroom
// Timing: did the hit land where the score says?
.onset_at("drums", bar(17).beat(1), Ms(5.0))
// Intonation: does every note read as written? (monophonic tracks)
.pitch_matches_score("lead", Cents(10.0))
// Was the sweep *written*? (authored curve, block-rate — a score lint)
.monotone("lead", Param::CUTOFF_HZ, bar(1)..bar(3))
// ...and did it audibly *happen*? (rendered stem's spectral centroid)
.brightness_rises("lead", bar(1)..bar(3), 1.3)
// Do the hits land on the detected beat grid?
.grid_alignment_at_least(0.9)
// Click detection away from note boundaries:
.no_discontinuity("lead", Db(40.0))
// Does the piece actually end?
.silent_after(bar(64))
.run();
同样的断言也可嵌入 RON 记谱中:
verify: [
IntegratedLufs(target: -14.0, tol: 0.5),
TruePeakBelow(dbtp: -1.0),
OnsetAt(track: "drums", at: (17, 1), tol_ms: 5.0),
PitchMatchesScore(track: "lead", tol_cents: 10.0),
Monotone(track: "pad", param: "cutoff_hz", from: (1, 1), to: (3, 1), direction: Rising),
BrightnessRises(track: "pad", from: (1, 1), to: (3, 1), min_ratio: 1.3),
NoDiscontinuity(track: "lead", db: 40.0),
SilentAfter(at: (64, 1)),
TempoIs(bpm: 110.0, tol_bpm: 2.0),
HasClearRhythm(expected: true),
GridAlignmentAtLeast(min: 0.9),
]
运行 cochlea render score.ron --verify 即可执行这些断言;失败时会返回 JSON
({"passed": false, "checks": [...]})并以非零状态码退出。
若要实际达到响度目标,而不只是断言它,为乐谱添加一个主总线——用增益提升音量,用限制器控制峰值上限:
master: Master(
gain_db: 4.0,
limiter: Limiter(ceiling_db: -2.0), // sample-peak ceiling holds exactly
),
verify: [
IntegratedLufs(target: -14.0, tol: 0.5),
TruePeakBelow(dbtp: -1.0), // ~1 dB headroom over the ceiling: true peak is inter-sample
]
demos/ 中有四个完整示例:
metronome— 精确到采样的调度和起音容差。chord_pad— 和声回放与记谱一致。title_cue— 一个四小节的电影风格片段,可断言 LUFS 目标、单调滤波器扫频、无咔嗒声,以及渐弱后的静音。drum_groove— 以真实的kick和snare音色演奏的 110 BPM 八小节律动,镲片声像位于右侧,军鼓位于左侧。它可断言检测到的速度、HasClearRhythm(true)且网格对齐度 ≥ 0.9、立体声宽度、响度范围和段落数。
drum_groove 也是促使我们将速度与节奏拆分的示例:旧的单一置信度指标虽然准确识别出 BPM,却以 0.01 的分数判定它没有节奏。
工作区
crates/
score # IR: ticks, tempo map, bar/beat math, notes, automation, master, RON form, MIDI import
synth # Patch trait over fundsp, eleven presets, param registry, counter RNG
render # block engine, voices, stems, f64 master sum + gain/limiter, WAV out
features # LUFS/true peak, onsets, pitch+melody, timbre, chroma/key, tempo, rhythm, stereo, structure
decode # WAV + FLAC (bit-exact) + mp3 + ogg (analysis) -> Audio, pure Rust
spectro # mel spectrogram -> PNG, overlays, diff heat maps, contact sheets
verify # assertion DSL + RON-embeddable specs + JSON reports
cli # the `cochlea` binary
mcp # MCP stdio server (agents call compose/render/probe/verify as tools)
features 和 spectro 都不依赖 score 或 synth——这一点由 CI 强制执行——因此 probe 可以处理任意音频文件,无需提供记谱。
许可证
MIT 或 Apache-2.0,任选其一。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。