mcpField
C
Cochlea

Cochlea

本地服务
github.com/richer-richard媒体与设计156 周前更新

关于 Cochlea

Cochlea 是面向 Agent 和音频开发者的离线音频引擎,可用于创作、渲染、检查和验证音频。它将音频转换为精简报告和频谱图,并支持确定性渲染及常见格式间的特征比较。

主要功能

  1. 01用 RON 数据编写乐谱
  2. 02离线确定性音频渲染
  3. 03分析响度、音高、速度与节奏
  4. 04生成频谱图和特征报告
  5. 05按感知特征比较音频
  6. 06导入和导出 MIDI

适用场景

  1. 01依据内嵌断言验证渲染结果
  2. 02检查长音频文件中的指定片段
  3. 03比较重新渲染后的音乐变化
  4. 04通过 JSON 报告或频谱图检查音频

MCP 相关

stdio MCP 服务提供 12 个工具,支持渲染、探测、频谱图、乐谱检查、音频比较、MIDI 转换、转录和乐谱参考。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库
安装包
mcpbcochlea-mcp.mcpb0.7.0

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

cochlea

CI 文档

cochlea:字标填充了 first_light.ron 的梅尔频谱图,背景是一个会渲染、探测并验证乐谱的 Agent 会话

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

first_light.ron 的梅尔频谱图:六个音符起音,随后是逐渐衰减至静音的混响尾音

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)→ 频谱图(一次视觉调用)→ 验证

  1. 编写乐谱数据(RON,或上文的 Rust 构建器)。
  2. 渲染为确定性的 PCM —— cochlea render score.ron --out mix.wav。
  3. 探测混音并生成精简的 JSON 报告(响度、起音、音高、调性、静音、削波)—— cochlea probe mix.wav --json report.json。 不需要图像或音频:Agent 读取数字即可。
  4. 查看,当数字不足以说明问题时—— cochlea spectro mix.wav --out spec.png 会渲染一张小型 PNG,供 Agent 通过一次视觉调用查看,无需对原始采样进行推理。
  5. 验证—— 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:

title_cue 演示的 Mel 频谱图:随着滤波器扫描让更多高频能量通过,画面顶部的安静频带在前两个小节逐渐变窄

随着扫描进行,画面顶部的暗色频带逐渐变窄,让更多高频能量随时间通过。Agent 可以直接从图像中读出这一点。演示中的 Monotone(track: "pad", param: "cutoff_hz", ...) 断言会以数值方式检查同一现象。

无论乐曲多长,都可以通过 --sheet 将频谱图平铺成一张联系表,而不是生成一条长带。这里通过 --bars-per-tile 2 设置每个平铺块包含两个小节:

first_light.ron 的联系表频谱图,每行平铺两个小节

无需上下文窗口即可读取音频

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 msBPM 精确,对齐度 1.0,clear_rhythm 保持成立(清晰度 0.77 / 0.51)
节奏鲁棒性点击音轨,抖动 ±20/±30 msBPM 折半至半速(节拍变得模糊,使两拍延迟与一拍延迟同样清晰)— 但对齐度仍为 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,任选其一。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。