关于 earshot
earshot 是供 Agent 使用的音频分析技能,可通过命令行、Python API 或 MCP 检查音频素材、混音和视频音画同步。模型在本机 CPU 上运行并缓存结果;可选的 `llm` 命令除外。
主要功能
- 01测量响度、频段及起音和衰减
- 02描述、匹配、排序和比较音频
- 03查找片段并评估真实感
- 04分析视频事件、音频和画面同步
- 05生成标注图表并评估混音
- 06通过代码合成并迭代声音
适用场景
- 01从音效文件夹筛选符合描述的素材
- 02检查影片音效与画面剪辑的同步
- 03比较混音修改前后的声音特征
- 04按参考录音制作并评估合成音效
MCP 相关
earshot 提供 MCP 服务,暴露音频测量、描述、匹配、排序、查找、比较、时间线、图表和评估等工具。服务进程可在多次调用间保留已加载的模型。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ leonxlnx/ earshot --skill earshot安装后在 Claude Code 中输入/earshot确认已加载。
详细介绍
earshot:Agent 的耳朵
你听不见。没有 earshot 的测量结果作依据,就绝不要描述音频听起来如何,也不要说某个声音「很合适」;汇报时要引用数值(「匹配度高,z +3.9;p_real 0.96;-14.2 LUFS」)。earshot 在本地 CPU 上运行(除非使用可选的 llm 命令,否则音频绝不会离开本机),并将每个嵌入和结果缓存到磁盘。成本:每次 CLI 调用都会启动新进程并先加载模型(即使文件已缓存,在负载较高的笔记本电脑上也要约 20-40 秒);在 MCP 服务或单个 Python 脚本中,模型只加载一次,后续调用只需几秒。调用次数超过少数几次时,请使用 MCP 或简短脚本(from earshot import api; api.find(...); api.realness(...))。
设置(一次性)
pip install -e "/path/to/earshot[mcp,llm]" # or the repo venv: /path/to/earshot/.venv/bin/python -m earshot ...
earshot doctor # packages, ffmpeg, models (pinned revision, cache state); --load times them
有两种接入方式,使用相同的函数:
- CLI:
earshot <command> ... [--json] [--out result.json]。文本输出以简洁摘要开头;文本中的时间格式为 m:ss.ss,JSON 中则以秒为单位。 - MCP(模型会在多次调用间保持加载状态):
claude mcp add earshot --scope user -e EARSHOT_THREADS=3 -- /path/to/earshot/.venv/bin/python -m earshot mcp,或者在任何客户端的.mcp.json中配置:{"mcpServers": {"earshot": {"command": "/path/to/earshot/.venv/bin/python", "args": ["-m", "earshot", "mcp"], "env": {"EARSHOT_THREADS": "3", "GEMINI_API_KEY": "${GEMINI_API_KEY:-}"}}}}(仅可选的 llm 工具需要配置密钥;Claude Code 会从用户环境中读取该密钥,其他客户端可能需要在启动它们的环境中设置密钥:切勿将明文密钥写入项目文件)。 工具包括:measure、describe、match、rank、find、compare、ab、realness、timeline、sheet、critique、doctor、 llm_critique;音效库(不需要模型):sfx_list、sfx_render、kit_render(Loop 5)。传入绝对路径;full=true会返回全部 JSON。服务通常会在几秒内响应;冷启动或机器繁忙时会更久。如果 Claude Code 中没有显示 earshot 工具,请使用MCP_TIMEOUT=60000启动它(PowerShell:$env:MCP_TIMEOUT=60000; claude)。earshot mcp --warm会在启动时预加载模型。 如果机器由多人共用,请设置EARSHOT_THREADS=2-3(或者在任意命令中使用--threads 2)。进程首次调用模型时会加载 CLAP + AST(约 20-60 秒);同一进程中的后续调用会很快。任何 ffmpeg 可读取的文件都可以处理,包括 mp4 中的音频(如果视频没有音轨,会明确说明)。错误信息会以单行形式输出到 stderr:earshot <command>: error: ...,退出码为 1。
第 1 轮:选择素材
earshot rank "a soft felt mallet hit on a wooden block" downloads/sfx/ --top 5——从文件夹中筛选候选素材。earshot find winner.ogg "a single clean mallet hit" --top 3——找出最合适的片段,作为样本裁剪(起始/结束时间以秒为单位)。每个片段都有类型:stroke(一次起音,直到声音衰减)和event(属于同一事件的一组击打,例如按键按下 + 抬起),起始时间设在起音前 5 ms,结束时间设在衰减之后;window是一段片段,其边缘会自动对齐到声音上,用于单次音效查询(用于背景音时,则是可交叉淡化的普通片段)。head/tail表示最初 5 ms/最后 10 ms 相对于裁剪片段峰值的响度(-30 dB 或更低 = 干净);标记为“head cut”或“tail cut”的裁剪片段会从声音中间开始或结束:不要直接使用。--kind event只返回基于起音的裁剪片段(敲击声、咔哒声、脚步声),--kind texture只返回普通片段(背景音);默认值会根据查询内容判断。earshot describe winner.ogg --start 1.12 --end 1.48——了解声音的实际听感;检查顶部标签是否符合需求,以及声音形态(起音/衰减)是否符合预期。AudioSet 对约 0.5 s 以下的单次音效标签较弱:“tagger unsure”或近似类别(比如把呼啸声标为 Zipper、把咔哒声标为 Finger snapping)属于中性结果;只有完全不同的类别(Speech、Music、Engine、Vehicle)才是警示信号。earshot realness winner.ogg --start 1.12 --end 1.48(或针对整个文件夹)——评估你准备使用的裁剪片段:对于包含多次录音的文件,p_real 是所有录音片段及其间静音的均值。如果用户需要真实录制或专业制作的声音,不要使用“likely code-synthesized”的裁剪片段;使用“uncertain”的片段前,先阅读证据(并写明保留它的理由)。如果合成声音本身就是目标(程序化音乐、UI 音效、用 earshot forge 制作的声音),真实度只是参考信息,不是筛选门槛。判定阈值取决于时长(每条摘要都会列出阈值):不超过 6 s 的声音使用片段阈值(低值 0.17/高值 0.77);较长文件使用其 4 s 窗口的均值,并采用校准较弱的文件阈值(低值 0.40/高值 0.77),因此比较不同长度文件时应比较判定结果,而不是原始 p_real。真实录音以及专业制作的音乐和声音设计通常会被判定为真实,但真实度判断的是声音听起来如何,而非它的来源:干净的专业合成音效(UI 音效、钟声、合成器短促音)也会被判定为代码合成。如果真实度检测不可用(缺少模型),请明确说明未检查真实与合成;describe 给出的 AudioSet 类别(Sine wave、Beep、Synthesizer)只能作为较弱的证据。仅凭精确的数字零无法证明任何事情(该项目获得授权的真实录音中,有 59% 带有编辑或淡入淡出留白)。- 要在两个候选素材之间做选择:
earshot ab a.ogg b.ogg --query "a crisp fast air whoosh",对于从 find 中裁剪的片段,可使用--a-start/--a-end/--b-start/--b-end(A 和 B 可以是同一个多次录音文件)。可选:使用earshot match winner.ogg "a deep thocky key press" --alts "a clicky blue switch" "a typewriter key"测试一项判断,或使用earshot compare winner.ogg --refs approved/ [--start --end]检查它是否符合素材风格。
循环 2:审查混音或影片
earshot timeline film.mp4 --cues cues.json(或soundtrack.wav --video film.mp4)——聆听转录: 事件及其时间、音量和标签,片段、响度曲线、画面剪切,以及每个剪切点/提示音的同步情况。--detail low速度较快,high最多标记 96 个事件(最重要的优先;摘要会说明标记了多少个事件,因此未标记的事件不代表缺少声音)。查看同步统计数据:offset_ms = 声音时间减去画面时间(负值 = 提前),而“chance”比例表示随机剪辑点能达到的分数;只有优于随机水平的同步才有意义。列出的剪切点即使没有命中,也可能带有轻柔的呼啸声:添加声音前先在图表上检查。earshot sheet film.mp4 -o sheet.png --cues cues.json(--from 10 --to 25可放大查看,--detail low速度更快)——一张带注释的 PNG:画面帧、片段、带标签的事件、频谱图、响度、节拍,以及按同步情况着色的提示音标记(绿色 = 在一帧内,琥珀色 = 在 100 ms 内,红色 = 没有声音)。用图像查看器打开 PNG:空档、堆叠、掩蔽和延迟命中一目了然。earshot critique film.mp4(--fast会跳过 AudioSet/真实性检查)——与专业发布影片的测量范围对比的评分卡:每项指标标为正常/偏低/偏高,并列出其权重和带时间戳的操作建议,以及结论(达到发布标准/接近标准/需要改进/未达到发布标准)。先修复关键项,然后对新渲染版本重新运行 1-3。其“onset on visual events”比例只统计 +-100 ms 内的新起音(参考资料中的定义);其修复建议和提示会通过与 timeline 相同的匹配器,指出完全没有声音的剪切点。如果文件是该配置文件自带的参考影片之一,critique 会说明这一点,并与其他影片进行评分对比。- 随时精确测量各项指标(LUFS、true peak dBTP、LRA、噪声底、起音/衰减、明亮度、频段):
earshot measure mix.wav [--music]或 MCPmeasure工具。
循环 3:对比更改前后
earshot ab before.wav after.wav --query "warm punchy kick"或--refs pro_refs/——找出哪个更接近目标,以及 B 相比 A 在测量指标上的差异(更响、更明亮、衰减更长)。对于较长文件中的声音,传入裁剪范围(--a-start 1.217 --a-end 1.615 --b-start 7.881 --b-end 8.285):对包含多次录制的文件进行整段 A/B 对比,会比较每次录制以及其间的静音。然后运行earshot timeline after.wav,并将事件行和同步统计数据与旧结果对比。一次只改一项。
循环 4:用代码制作声音(earshot forge)
当用户想要制作声音,而不是挑选声音时使用:能听声音的 AI 可以编写合成代码、聆听并改进。每个样本都必须通过计算生成(不能包含录制的音频,甚至不能包含脉冲响应);真实录音只能作为评判器比较的参考。
-
在
earshot/forge/recipes/<name>.py中编写或挑选一个配方:SPACE(参数范围)、DEFAULTS、TARGET、ALTS(容易混淆的真实声音)和render(params, r) -> 48 kHz array,基于earshot.forge.dsp构建(模态谐振器、接触激励、噪声色彩、时变 SVF、计算生成的房间、带噪声底的麦克风)。根据参考录音测量结果,模拟真实物体的物理特性:什么撞击什么、什么会共振、持续多久、处于什么房间。 -
运行一轮:
earshot forge <recipe> --refs <real refs> --budget 144 --reps 2 --tone 1.5 --keep 4 --robust 8 --dir <round dir>;或者每轮使用一个 Python 脚本,只加载一次模型:forge(recipe, critic=Critic(target, alts, refs, tone=1.5), reps=2, space={...})。--tone会将无模型的音色平衡和余音纳入目标。--reps 2会为每个候选生成 2 次渲染,并据此排名,以免被偶然的幸运渲染左右。- 在繁忙的笔记本 CPU 上,每 160 次渲染约需 3-5 分钟,其中包括 30-60 秒的模型预热时间。
-
阅读听感结果。每个候选的摘要会显示,例如“听起来像:软木塞爆裂声、玻璃叮响声”“衰减比参考录音长 +2.4”或“音色与参考录音中位数相差 11.2 dB:500-630 Hz 处高出 +16 dB(超过所有参考录音)”。然后从四个方面检查候选:
- 无模型的
tone描述:哪些频段超出所有参考录音、能量占比、余音、长度(模型评分较高后,这是最值得信赖的信号) robust:新种子的平均值,而非最幸运的最佳渲染alt_zs:最接近的是哪种混淆声音- 与参考录音并排查看频谱图:
ffmpeg -i x.wav -lavfi showspectrumpic=s=900x360:scale=log:legend=0 x.png
只要你不根据 report.json 中的 AudioSet 标签挑选或引导生成,这些标签就不会参与评估。先确定选择规则(例如稳健均值),再查看标签,并在之后报告标签。
- 无模型的
-
针对问题修改配方的结构,而不只是参数范围(例如缺少释放时的轻响、金属共鸣、余音过长)。然后重复操作。记录每轮修改和数值,保留 LOG。
-
诚实报告结果,并列出同一评判器对真实参考录音的评分(
report.json中的 ref_scores,留一法)。
直言 Goodhart 定律:
- 搜索会优化 earshot 自己的模型。制作的声音在模型上的得分往往高于真实录音,这说明评判器满意了,并不代表声音更好。
- p_real 很容易被欺骗:模态谐振器加上房间效果和麦克风噪声底,就会被判为“真实”。
- 典型性会奖励平均值:越接近参考录音的平均水平,得分越高。
- 模型检查会饱和:搜索之后,包括留出的措辞和独立真实数据集在内的每项模型检查,都会把制作的声音判定为真实声音。只有测量结果和人耳还能分辨两者。
- 根据与参考录音的实测接近程度进行评判:音色平衡、超出所有参考录音的频段、余音、长度。也要使用稳健的重新渲染结果和频谱图。
- 在宣称制作的声音“和真实声音一样好”之前,先进行人工盲测(随机排序、响度匹配、隐藏答案)。
在首次测试(demo/forge-test)中,独立评判者发现,按键声的音色平衡与真实录音相差 8-13 dB,爆裂声的频段没有限制且带有绵长的嘶嘶余音,尽管所有模型检查都将它们判为真实声音。后来根据无模型检查结果引导重新制作,这些声音现在的实测值都落在真实录音的范围内。呼啸声被重新制作成真正迅疾的声音。冲击声(更低沉、由渐强声引领)和咔嗒声(余音较短)没有重新制作。还没有人听过这些声音。
Loop 5:制作影片音效包(earshot sfx / earshot kit)
如果代码制作的影片(cinetic film、任何提示表)需要音效,请使用此流程:从 earshot 随附的音效库渲染,而不是编写合成器音色。无需加载模型;整个音效包约一分钟即可渲染完成,重新渲染只需几秒。
- 从预设开始:
earshot kit init --preset calm -o audio/kit.json(calm 适用于文字主导的影片,crisp 适用于节奏明快的产品影片,minimal 适用于经过设计、仅含音效的影片,launch 适用于配乐剪辑的影片)。每个角色的note都会说明选择该声音的原因。calm / crisp / minimal 不包含 riser、swell 和 suck:揭示画面由单个元素收束,片尾标识则使用干净利落的一击。launch 在段落切换时使用一个 riser 或 reverse swell,并使用专为配乐主导的影片制作的 0.4 音效(见下文)。 - 根据影片调整:
earshot sfx list --kind hit(或--category)会显示候选音效及其时长、峰值时间和可调参数。更改角色的sound,设置edits(pitch设为影片的根音,用于 hit 或 drop;dur/apex用于固定动作)、takes(打字音效设为 12 次或更多,其他音效设为 2-4 次)、level_db。试听单个候选音效:earshot sfx render ID -o x.wav [--pitch E5] [--dur 0.8 --apex 0.32],并查看测得的起始时间 / 峰值时间 / 结束时间。 - 渲染:
earshot kit render audio/kit.json --out audio/kit。混音器会根据提示帧放置各个 take 和kit.lock.json(记录每个 take 测得的起始时间、峰值时间、结束时间和响度):hit 和触碰音效的起始时间对齐提示帧,whoosh 的峰值对齐其顶点,riser 的结束时间对齐其 hit(角色对齐到peak时,则对齐其峰值;launch 的 riser 和 swell 就是这种情况:这些音效在高潮后逐渐减弱;kit check会标注与结束时间对齐的音效)。 - 检查:
earshot kit check audio/kit.json --out audio/kit。此命令会拒绝渲染时无法使用的配置(音效不支持的旋钮或音高、高于 0 dBFS 的音量、两个会共用同一文件的 take)。每条警告都会指出 take 和原因(咔嗒声、尾音被截断、梳状滤波振铃、静音、音高上升超过 4 个半音、有两个起音或起音前已有渐强、pitch_off:旋钮没有调到目标音符);请更换音效或旋钮,不要保留带警告的 take。也要阅读提示(已听到的 riser、复合提示音)。 - 使用 Loop 2(
timeline --cues、sheet)完整试听成片,并确认每条提示都已正确落点。
0.4 系列(earshot sfx list --category ui_pop 等),适用于配乐主导的发布影片,呈现柔和、短促、中音域的声音:ui_pop(圆润的中音 pop,适用于芯片、气泡和逐行出现的内容:此类影片中最常用的音效)、knock(中频木质和塑料敲击声)、cut_hit(带有低音鼓的短促低频 hit,富有冲击力的剪切 hit)、mallet(门控式钟琴 / 钢片琴 / 木琴音符、轻柔拨弦声、钟声音阶;可通过 --pitch 调音)、drip(音高上升的水滴声:审核允许气泡音高上扬)、shutter(快速变焦、甩切)、reverse_swell(在剪切后达到峰值,每部影片使用一个),以及 whoosh_big thump*、riser dark / build / snatch、swish bright / flick、whoosh_fast whip、glitch burst / mid / chirp 和 notify mid_tine / mid_wood。影片按音乐剪辑时,优先使用这些音效,而不是旧分类中明亮的 5 ms 短促提示音和冗长的电影感尾音。
音量应依据音乐调整,而非固定不变(launch,或任何带有 bed_db 的角色)。配乐剪辑的影片中,音效通常比音乐高 3-10 dB,并通过时机和频谱被听见:固定的 level_db 在音乐响亮的段落中可能会被淹没,在安静的段落中则会过于突出。对每条提示测量配乐电平,并根据锁定信息设置 take:
from earshot.forge import kit
lock = kit.render('audio/kit.json', 'audio/kit') # takes carry l50_lufs; roles bed_db, offset_ms
role = lock['roles']['pop']; take = role['takes'][i]
bed = kit.bed_level(music, t_cue) # the music's median 50 ms level, second before
gain = kit.gain_db(take, role, bed) # dB on the take file (never over -1 dBFS peak)
start = kit.place(take, role, t_cue) # taps / knocks / clicks lead, whoosh peaks land after
cues = kit.space_run(run_times, role['min_gap_ms']) # a run keeps its gap (pops 114 ms, keys 87)
在 hit、drop、大型 whoosh 或 swell 之前的 0.3-0.5 s 内,将音乐音量降低 6-12 dB,并在 hit 到来时恢复(锁定信息中的 mix.stop_down);不要在每个音效下都降低音乐音量。没有配乐(以音效为主的影片)时,level_db 是备用设置;提示前音乐静音时(例如影片冷开场或配乐间隙),bed_level 会返回 None,gain_db 会使用 level_db。请报告设置的增益和测量的配乐电平。
坦白说明音效库的内容:forge 会用代码合成声音(0.4 版的风格经过调校,贴近一组已获授权的参考音频片段的测量风格,并经检查确保仍是原创:与其中任一片段的 CLAP 相似度最高为 0.84);另有 15 种声音是根据 CC0 录音以代码拟合而成(fit/<role>/<name>,每种都附有其来源;预设用它们制作点击声、滴答声、按键声、触碰声和弹响声)。所有声音都经过测量筛选和检查,没有经过试听测试。它们不是录音:如果需求要求使用录制的声音,请改用 skills/real-sound。如需添加自己的拟合音效(earshot fit --takes,然后运行 bank add-fit),请参阅 docs/SFX.md(“Add your own fit”)。
可选:用文字征求第二意见(音频大模型)
earshot llm mix.wav --yes-upload [--with-timeline] [--prompt "Is the riser at 0:12 cheap?"] 会请求 Google Gemini 按评分标准进行评析(同步、真实感、混音、响度、哪些声音听起来廉价,以及带时间点的改进建议)。仅当用户已设置 GEMINI_API_KEY(或 GOOGLE_API_KEY),并明确同意将音频上传至 Google 时才会运行;请先征求同意,切勿自行传入 --yes-upload / consent=true。只会发送音频(单声道,64 kbps)。时间戳为近似值,评分也只是一个模型的意见:请通过时间线 / ab 确认每项改进建议。
解读数值
| 数值 | 含义 | 可信度 |
|---|---|---|
| LUFS、dBTP、LRA、dBFS、ms、Hz | 测量值(BS.1770 测量仪、频谱、包络) | 精确;用于判断电平 |
| z(match / rank / find / describe 标签) | 相对于在同一声音上评分的数百条词汇描述,某条描述的突出程度 | 判断标准:z >= 3 表示强,2.5-3 表示较可信,2-2.5 表示较弱,< 2 表示不匹配(各频段的留出证据见 eval/semantics/RESULTS.md) |
| p_vs_alts | 对你的查询与备选项进行的未经校准、判别性很强的强制选择 | 只表示哪种措辞最匹配;请依据 z 判断 |
| cosine | 原始 CLAP 相似度 | 绝非概率;只能针对相同的声音和查询进行比较 |
| AudioSet 标签(0..1) | AST 标注器给出的各类别独立概率 | 适合判断宽泛类别;只能听到最高 8 kHz |
| p_real / verdict | 基于小型拟合模型,对真实录制或专业制作与代码合成进行判断 | 是有力证据,但不是定论;请阅读证据列表;片段(<= 6 s)和长文件的阈值不同(各摘要中有说明) |
| digital_silence_frac | 精确为零的样本占比 | 单独看时参考价值较弱:真实录音中常见剪辑 / 淡入淡出留白 |
| typicality z(compare) | 与参考集的距离,相对于参考音频自身的分散程度 | >= -1 表示符合该集合,< -2 表示离群值 |
| sync offset_ms、chance | 每个剪辑 / 提示点的声音与画面偏移;以及随机剪辑可能得到的分数 | 明显高于 chance 时才可信 |
| critique status | 相对于 7 部专业发行影片的取值范围(响度:网络交付惯例;母带真峰值 <= -1 dBTP,解码后的 AAC / MP3 <= 0 dBTP,后者已计入编解码器造成的超调) | 表示范围而非目标:ok = 处于专业作品的常见范围内 |
优先参考测量值,其次是判断结果 / z / 概率;绝不要把 cosine 当作置信度。
编写查询
描述声音本身,而非用途:声源 + 材质 + 动作 + 特征(“小房间里一扇厚重的木门砰然关上”,而非“用于标志的声音”)。提供实际可能混淆的备选项(闷响 vs 点击声 vs 打字机声)。当 z 接近临界值时,试试两三种说法:只有在所有说法下都成立的结果才值得信任。
局限(相关时请直说)
- 品味无法量化:earshot 会说明声音是什么以及它的测量结果,但不会判断它是否悦耳,或是否适合这个故事。品味由用户决定;请提供证据和候选清单。
- 不支持语音识别(只能判断是否存在语音,无法识别说了什么),也不会判断歌词或语言。
- CLAP 可听到 50 Hz - 14 kHz,AST 的上限为 8 kHz:请通过测量(bandwidth_hz、bands)检查“空气感”和低通问题。
- 非常短的声音会填充至 10 s 的窗口;使用 find / describe 处理裁剪后的片段,最适合读取孤立的瞬态声。
- attack_ms / decay_ms 根据平滑后的包络计算(5 ms / 10 ms RMS):瞬时起音会显示为约 4 ms,而非常短促的咔嗒声无法读取低于约 10-20 ms 的衰减时间。
- 画面切点由帧差检测器识别:硬切和快速摇镜头的结果可靠,慢速交叉淡化可能漏检或延迟检测;镜头内部的运动不算事件,因此动画应传入提示时间(--cues)。
- 混合、密集的素材会模糊标签:请描述单独的音轨或片段,而非整个混音。
- 许可、来源和署名不是音频问题:earshot 无法核查这些信息。