关于 Audio
Audio 是面向开发者和音频处理流程的 JavaScript 音频编辑与分析库,提供 CLI。它结合流式与分页处理、非破坏性编辑、播放和分析,且无需 ffmpeg。
主要功能
- 01140 多种音频处理插件
- 02支持撤销的非破坏性编辑
- 03流式播放与编码
- 04处理大型音频文件的分页机制
- 05响度、频谱、节拍与音高分析
- 06用于批处理和脚本的 CLI
适用场景
- 01规范并检查播客响度
- 02修剪、降噪和均衡录音
- 03分析节拍、音高或调性
- 04在脚本中批量处理音频
MCP 相关
Audio 提供 MCP 服务,向 MCP 客户端开放测量、查看、编辑、选择、播放和检查音频等工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
1收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| args | string | 是 | `audio` 后的参数,使用 shell 引号括起,并使用绝对路径:"/Users/me/voice.m4a trim normalize podcast save /Users/me/voice.mp3" |
详细介绍
audio

高级音频处理:加载、播放、分析和编辑。提供 140 多个插件:降噪、动态处理、EQ、滤波器、效果、混响、时间与音高、空间音频、合成、音乐分析。
- 任意格式 — 快速的 wasm codecs,无需 ffmpeg。
- 非破坏性 — 虚拟编辑、无限撤销、即时克隆。
- 流优先 — 解码期间即可播放/编码,支持实时编辑。
- 分页处理 — 没有 2Gb 内存限制,可打开 10Gb 以上的文件。
- 分析 — 响度、频谱、节拍、音高、和弦、调性。
- 模块化 – 可插拔操作,支持 tree-shaking。
- CLI — 播放、批处理、脚本、unix 管道、制表符补全。
- 跨平台 — 浏览器、node、deno、bun。
开始使用
Node
npm i audio
import audio from 'audio'
let song = audio('song.mp3')
song.play() // the speakers, as a page plays it: no ffmpeg, no player to spawn
song.pause(); song.seek(60); song.resume()
let voice = audio('voice.wav').trim().normalize('podcast')
await voice.save('clean.mp3')
let { pass, rules } = await voice.check('podcast') // each rule of Apple's spec, measured
浏览器
<script type="module">
import audio from 'https://esm.sh/audio'
audio('./song.mp3').trim().normalize().fade(0.5, 2).clip({ at: 60, duration: 30 }).play()
</script>
随处使用 Audio
import 'audio/polyfill' // the page's HTMLAudioElement, in Node, Bun and Deno
let song = new Audio('song.mp3')
song.onended = () => console.log('done')
await song.play()
song.currentTime = 30; song.volume = 0.5; song.loop = true
事件、事件顺序、Promise 和错误都与 Chromium 自带元素的行为一致,并逐项进行了对比观察(test/polyfill.js)。import { Audio } from 'audio/polyfill' 不会影响全局对象。
CLI
npm i -g audio # or: npx audio …
audio voice.wav trim normalize podcast save clean.mp3
audio clean.mp3 check podcast
# Apple Podcasts https://podcasters.apple.com/support/893-audio-requirements
# ✓ Loudness -16.44 LUFS -17 to -15
# ✓ True peak -1.18 dBTP ≤ -1
# pass
失败时会以状态码 1 退出,因此同一行命令也能用于构建检查:在 CI 中检查。
技能
npx skills add audiojs/audio
MCP
npx add-mcp "npx -y audio --mcp" # asks which of your agents: Claude Code, Codex, Cursor, Gemini CLI, Kimi Code, Pi, OpenCode, Zed…
提示词:让 ~/Desktop/interview.m4a 达到播客发布标准,并告诉我处理前后的响度
一次只能使用一个 Agent:claude mcp add audio -- npx -y audio --mcp、qwen mcp add audio npx -y audio --mcp、droid mcp add audio "npx -y audio --mcp"。
Playground 和 Agent
npx audio --bridge
# audio bridge on http://127.0.0.1:7777
#
# key 3e37b8c61acf5b50f010852168f4843d
# agents Claude Code, Codex, Pi, Kimi Code
#
# In the playground's Agent panel, paste the key, then Connect. It stays the same next time.
打开 playground,每次编辑都会生成一行 audio 代码,然后使用密钥连接到 bridge(只需连接一次:bridge 会保留密钥)。其中的聊天会运行一个属于你的 Agent,它可以测量、查看、编辑并播放当前打开的声音,还能找出每次编辑产生的效果:它会测量每次编辑前后的声音,以及每次编辑移除了什么;每个标签页都会保留自己的对话,每段对话都关联一个 Agent 和它的模型,可在消息下方选择。bridge 会在 PATH 中查找 Claude Code、Codex、Pi、Gemini CLI、Qwen Code、Kimi Code、OpenCode、Kilo Code、Cline、Goose、Factory Droid、Cursor、Augment、Kiro 和 Mistral Vibe;其他任何支持 ACP 的 Agent 都可以通过其命令行运行:--agent "my-agent --acp"。
任何 MCP Agent 都能使用 playground 的工具(state、measure、look、edit、select、play、check,……),并会自行找到正在运行的 bridge:npx add-mcp "npx -y audio --mcp --playground"。
Agent 使用自身设置中指定的模型进行思考:Pi 可通过 ollama launch pi --config 永久使用本地模型;Claude Code 则可通过 bridge 的环境变量使用任何兼容 Anthropic 的端点:
ANTHROPIC_BASE_URL=http://localhost:11434 ANTHROPIC_AUTH_TOKEN=ollama ANTHROPIC_API_KEY= ANTHROPIC_MODEL=qwen3-coder npx audio --bridge
| 模型 | ANTHROPIC_BASE_URL,并将提供商的密钥设为 ANTHROPIC_AUTH_TOKEN |
|---|---|
| Ollama | http://localhost:11434,令牌 ollama(文档) |
| Z.ai GLM | https://api.z.ai/api/anthropic(文档) |
| Kimi | https://api.moonshot.ai/anthropic(文档) |
| Qwen | https://coding-intl.dashscope.aliyuncs.com/apps/anthropic,Coding Plan(文档) |
| DeepSeek | https://api.deepseek.com/anthropic(文档) |
嵌入
可将 playground 嵌入任何页面,脚本就是元素自身的文本:读者滚动到该元素时,它便会运行、播放并应用编辑,还可在 playground 中完整打开。不会保存读者的任何内容。
<script type="module" src="https://audio.js.org/embed.js"></script>
<audio-repl panel="code" display="wave" height="320">
<pre><code>audio('chime.wav')
.trim()
.normalize(-1)</code></pre>
</audio-repl>
panel:code(默认)、edits(显示步骤及各自的设置)或 none;display:wave 或 spec;height 以 px 为单位。在框架加载前,以及脚本无法运行的地方,会显示 <pre> 内容。
配方
清理
// master a raw take
let a = audio('raw-take.wav')
a.trim(-30).normalize('podcast').fade(0.3, 0.5)
await a.save('clean.wav')
// full restoration chain via ecosystem plugins (see API › Plugins)
a.gate(-45).dehum().deesser().compressor({ threshold: -18 }).limiter({ ceiling: -1 })
// cut 2:00–2:15, smooth the splice
a.remove({ at: 120, duration: 15 }).fade(0.1, { at: 120 })
// find clipped blocks
let clips = await a.stat('clipping')
// does it pass? each rule of the spec, measured
let { pass, rules } = await a.check('podcast') // acx, podcast, spotify, broadcast, netflix
母带处理与交付
// master a song to a reference track: its tone (mid and side), width and loudness, under -1 dBTP
audio('mix.wav').master(await audio('reference.wav')).save('master.wav')
// audiobook chapter for ACX: RMS -23..-18 dB, peaks under -3 dB, floor under -60 dB, room tone at each end, 44.1 kHz
let ch = audio('chapter-01.wav')
.highpass(80).omlsa({ gMin: -12 }).compressor({ threshold: -24, ratio: 2.5 })
.normalize(-20, 'rms', { ceiling: -3.5 })
.trim().pad(1.5, 2).roomtone() // room tone, not digital silence
.resample(44100)
console.log(await ch.check('acx')) // passed 10 of 10 real narrations (.work/pro.md)
await ch.save('chapter-01.mp3', { bitrate: 192 })
// tighten pauses in a talking-head video, then hand the cuts to the video editor
let talk = audio('talk.mp4').shrink(0.3)
await talk.save('talk.m4a') // the sound, cut
await talk.save('talk.edl') // the same cuts for the picture (Premiere, Resolve)
编曲
// podcast montage
let ep = audio([intro, interview.trim().normalize('podcast'), outro], { crossfade: 0.5 })
await ep.save('episode.mp3')
// voiceover over music
music.gain(-12).mix(voice, { at: 2 })
// ringtone: the chorus + fades
audio('song.mp3').crop({ at: 45, duration: 30 }).fade(0.5, 2).normalize().save('ringtone.mp3')
// split an audiobook into chapters
let [ch1, ch2, ch3] = audio('audiobook.mp3').split(1800, 3600)
// glitch: stutter + reverse
let v = a.clip({ at: 1, duration: 0.25 })
audio([v, v, v, v]).reverse({ at: 0.25, duration: 0.25 })
分析
// waveform bars — and progressively, as it decodes
let [mins, peaks] = await a.stat(['min', 'max'], { bins: canvas.width })
a.on('data', ({ delta }) => appendBars(delta.max[0], delta.min[0]))
// features for ML
let mfcc = await a.stat('cepstrum', { bins: 13 })
let [loud, rms] = await a.stat(['loudness', 'rms'])
// notes, chords, key
let notes = await a.stat('notes') // [{time, duration, freq, midi, note, clarity}]
let chords = await a.stat('chords') // [{time, duration, label, root, quality, confidence}]
let key = await a.stat('key') // {tonic, mode, label, confidence}
录制与生成
// mic take
let a = audio()
a.record()
// …later
a.stop()
a.trim().normalize()
// tone — any t => sample function
let tone = audio.from(t => Math.sin(440 * Math.PI * 2 * t), { duration: 2 })
// sonify data
let s = audio.from(t => Math.sin((200 + data[t / 0.2 | 0]) * Math.PI * 2 * t) * 0.5, { duration: data.length * 0.2 })
自动化
a.gain(t => -12 * (0.5 + 0.5 * Math.cos(t * Math.PI * 4))) // 2Hz tremolo in dB
a.lowpass(t => 400 + 4000 * t) // filter sweep
a.pan({ t: [0, 2, 4], v: [-1, 1, -1] }) // curve L→R→L over 4s, serializable
music.ducker({ key: voice }) // sidechain (plugin)
流式处理与持久化
// stream to network — encode/playback during decode
for await (let chunk of audio('2hour-mix.flac').highpass(40)) socket.send(chunk[0].buffer)
// serialize edits, restore later
let json = JSON.stringify(a) // { source, edits, ... }
let b = audio(JSON.parse(json)) // re-decode + replay edits
API
创建
| 方法 | 说明 |
|---|---|
audio(source, opts?) | 从文件、URL、字节或字节流解码。立即返回——在后台解码;流式数据到达后即开始解码。 |
audio.from(source, opts?) | 封装现有 PCM、AudioBuffer、静音或函数。同步执行,不进行 I/O。 |
let a = audio('voice.mp3') // file path
let b = audio('https://cdn.ex/track.mp3') // URL
let c = audio(inputEl.files[0]) // Blob, File, Response, ArrayBuffer
let d = audio() // empty, ready for .push() or .record()
let e = audio([intro, body, outro]) // concat (virtual, no copy)
let f = audio([a, b, c], { crossfade: 2 }) // concat with 2s crossfade
let g = audio(process.stdin) // byte stream: pipe, socket, fetch body – decodes as it arrives
// opts: { sampleRate, channels, crossfade, curve, storage: 'memory' | 'persistent' | 'auto' }
await a // await for decode — if you need .duration, full stats etc
let a = audio.from([left, right]) // Float32Array[] channels
let b = audio.from(3, { channels: 2 }) // 3s silence
let c = audio.from(t => Math.sin(440*TAU*t), { duration: 2 }) // generator
let d = audio.from(audioBuffer) // Web Audio AudioBuffer
let e = audio.from(int16arr, { format: 'int16' }) // typed array + format
let f = audio.from(store, { length, channels, sampleRate }) // pages in a store ({ read(i), has(i), write(i, page) }), read as needed
属性
| 属性 | 描述 |
|---|---|
.duration | 编辑后的总秒数。 |
.channels | 声道数。 |
.sampleRate | 采样率。 |
.length | 每个声道的采样数。 |
.currentTime | 扬声器当前播放到的位置,单位为秒:已补偿延迟,且播放平滑;暂停时会停留在恢复播放的位置。 |
.playing | 播放期间为 true。 |
.paused | 暂停时为 true。 |
.volume | 0..1 线性音量。可设置。 |
.muted | 静音状态,与音量无关。可设置。 |
.loop | 可设置,播放期间也可设置:循环播放该片段,每个衔接处都会进行 10 ms 等功率交叉淡化。 |
.playbackRate | 范围为 0.0625..16,可在播放期间设置,且不会产生咔嗒声。除非 .preservesPitch 为 false,否则音高保持不变(WSOLA,与浏览器媒体元素以某个速度播放时一样);若为 false,音高会在约 50 ms 内平滑变化并随速度改变,呈现磁带式效果。.speed() 会将其烘焙到音频中。 |
.preservesPitch | true:速率不为 1 时,音高保持不变。false:变速播放,音高随速度改变。可在播放期间设置。 |
.ended | 播放到结尾时为 true;调用 stop() 后不会变为 true。 |
.seeking | 搜索期间为 true。 |
.played | promise,播放有声音时解析。 |
.recording | 录制麦克风音频期间为 true。 |
.ready | promise,完全解码后解析。 |
.source | 原始源。 |
.bitDepth | 源的存储采样深度:16、24、32(浮点);有损音频或生成的音频为 null。无损 save 会保留该值。 |
.pages | Float32Array 页面存储。 |
.stats | 每个区块的统计数据(峰值、均方根等)。 |
.edits | 编辑列表。 |
.version | 每次编辑时递增。 |
结构
| 方法 | 描述 |
|---|---|
.trim(threshold?) | 剪除开头和结尾的静音(单位为 dB,默认自动检测)。处理实时流时,指定阈值可边接收边输出,并在声音恢复前保留静音尾段;自动检测会读取完整输入,因此要等到输入结束。 |
.shrink(gap?, threshold?) | 将静音间隔缩短至 gap 秒(默认 0.3);设为 0 则移除静音间隔。指定阈值时可像 trim 一样处理实时流。≡ FFmpeg silenceremove、Audacity truncate-silence |
.crop({at, duration}) | 保留指定范围,丢弃其余部分。 |
.remove(at, duration, crossfade?) | 删除指定范围并闭合间隙。crossfade('10ms')会在剪切点周围进行等功率交叉淡化;总时长保持不变。 |
.insert(source, at?, crossfade?) | 插入音频(默认插入到末尾),或插入指定秒数的静音;crossfade 会淡化两个接缝处。 |
.copy({at?, duration?}) | 将指定范围(默认全部)复制到此实例的剪贴板。 |
.cut({at?, duration?, crossfade?}) | 先复制,再移除。 |
.paste({at?, crossfade?}) | 插入剪贴板中的内容(默认插入到末尾)。 |
.move({at, duration, to, crossfade?}) | 将指定范围滑动到 to,覆盖该处原有内容;原位置变为静音,长度保持不变(若移至末尾之后则延长)。crossfade 会在每个边缘进行交叉淡化,中心对齐。≡ DAW 中以滑移模式移动片段 |
.clip({at, duration}) | 创建零拷贝的新实例,包含指定片段。 |
.split(...offsets) | 在时间戳之间创建零拷贝片段。 |
.pad(before, after?) | 在边缘添加静音(单位为秒)。 |
.repeat(n) | 重复 n 次。 |
.reverse({at?, duration?}) | 反转音频或指定范围。 |
.speed(rate, {type?}) | 同时改变音高和时长;type: 'sinc' 使用 Lanczos sinc(16 个零交叉点)在采样点之间进行插值,而非线性插值;当速率接近 1 时,可保留最高八度。 |
.stretch(factor, {voice?, method?}) | 改变时长并保持音高(相位锁定声码器)。t => f 或 {t, v} 形式的因子会渐变节奏;时长变为 ∫factor dt。范围 {at, duration} 的输出长度为 round(round(duration · sampleRate) · factor) 个采样点,精确到采样点;范围周围的音频保持原样。{ voice: true } 会保留人声的脉冲形状和辅音,避免声码器处理后的人声变得遥远:缩短时会逐段复制波形(WSOLA,@audio/stretch-wsola);放慢时会从合适的波形位置重新启动声码器帧(PVSOLA,@audio/stretch-pvsola),因此呼吸声和混响不会重复成镶边效果。适用于单个人声,不适用于和弦。{ method } 会采用其他方式进行拉伸,且仅在此时加载对应模块:'vocoder'、'pghi'、'wsola'、'psola'、'sms'、'transient'、'hybrid'、'paulstretch'(@audio/stretch-*);默认的 'phase-lock' 支持流式处理、渐变和 voice。其他方式会一次性渲染要拉伸的内容,使用恒定因子;对于指定范围,会与两侧音频交叉淡化。≡ Logic Flex Time Monophonic、Ableton Tones |
.warp(markers) | 移动时间点:[[from, to], …],单位为秒。标记之间的音频会拉伸以适应,音高保持不变;开头和结尾保持不变。≡ Logic Flex Time、Ableton warp markers |
.pitch(semitones, {voice?, method?}) | 改变音高并保持时长。半音数可以是曲线 {t, v}(秒 → 半音,各点之间线性变化,两端之外保持平直,与增益线相同),也可以是 t => semitones;值为零时,音频保持原样。{ voice: true } 会根据人声的 pYIN 音高轨迹找到声门周期并重新排列这些周期(TD-PSOLA,可选的 @audio/tune-curve):保留共振峰和辅音,适用于单个人声;指定范围的边缘会按人声音高变化的速度滑动,周期也会重新对齐到输入音频。{ method } 会采用其他方式移调,且仅在此时加载对应模块(@audio/shift-*):'vocoder'、'formant'(保留人声音色)、'psola'、'wsola'、'lpc'、'transient'、'hybrid'、'hpss'、'sms'、'sample'、'delay'、'ola'、'granular'、'paulstretch' 或 'auto'(@audio/shift 会根据声音选择并加载所有模块);默认的 'phase-lock' 支持流式处理、跟随曲线和 voice。其他方式会按恒定的半音数一次性渲染移调结果;指定范围会在 10 ms 内淡入和淡出。≡ Melodyne 音高绘制 |
.intonation(factor) | 以人声的中位音高为中心,扩大或压平其音高起伏:1 表示保持原样,0 表示单调音高,2 表示起伏幅度加倍。重新排列人声自身的周期(与 pitch({ voice: true }) 相同):保留时序、共振峰和辅音,适用于单个人声。≡ Melodyne 音高调制、Praat 的音高范围因子 |
.formant(semitones) | 移动共振峰(频谱包络:人声的元音和头部大小),同时保持音高。参数可以是数字、曲线 {t, v} 或 t => semitones。适用于任何声音;与 pitch() 配合使用时,可保留人声本身,也可将其变为另一种人声。≡ Melodyne 共振峰工具、Praat 的共振峰移位比 |
.remix(channels) | 指定声道数(按 ITU-R BS.775 规则向下混音:7.1 → 5.1、立体声、单声道),也可以传入映射表:[1, 0] 交换左/右声道,null 表示静音声道。 |
每个操作都接受末尾的 {at, duration, channel} 范围,改变声道的 remix 和 crossover 除外;每个保留时间轴的操作都接受 mix:输出中被听到的比例,其余部分则是输入,取值为 0 到 1,也可以是随时间变化的曲线 {t, v}({ mix: { t: [11.99, 12, 14, 14.01], v: [1, 0, 0, 1] } } 会在 12 s 到 14 s 之间关闭效果,类似 RX 的 Restore Selection;效果自身的 mix 仅作用于该效果)。时间可以用秒或字符串表示('1:30'、'2m');负数从末尾开始计数。FFmpeg 的短名称与完整名称通用:d 表示 duration,xfade 表示 crossfade(a.remove({ at: 1, d: 0.5, xfade: 0.01 }))。 |
a.trim(-30) // strip silence below -30dB
a.remove({ at: '2m', duration: 15 }) // delete 2:00–2:15, close gap
a.remove(12.3, 0.4, '10ms') // cut a breath, crossfaded: no click
a.insert(intro, { at: 0 }) // prepend; .insert(3) appends 3s silence
a.copy(60, 30).paste(120) // duplicate the chorus at 2:00
a.cut(2, 1).paste(5) // move 2s–3s to 5s of the shortened timeline
a.move({ at: 2, duration: 1, to: 5 }) // slide 2s–3s over 5s–6s, silence left at 2s–3s
let [pt1, pt2] = a.split('30m') // zero-copy parts
let hook = a.clip({ at: 60, duration: 30 }) // zero-copy excerpt
a.stretch(1.1) // 10% longer, same pitch
a.warp([[1, 1], [2, 2.4], [3, 3]]) // the hit at 2s lands at 2.4s; 1s–3s keeps its length
a.pitch(-2) // 2 semitones down, same tempo
a.pitch({ t: [1, 1.2, 2, 2.2], v: [0, 3, 3, 0] }, { voice: true }) // a note drawn 3 semitones up, formants kept
a.intonation(1.5, { at: 2, d: 3 }) // 2s–5s: every rise and fall half as wide again
a.formant(-2) // a larger, darker voice on the same notes
a.remix([0, 0]) // L→both; .remix(1) for mono
处理
| 方法 | 描述 |
|---|---|
.gain(dB, opts?) | { unit: 'linear' } 接受乘数。 |
.fade(in, out?, curve?) | 曲线可选 'linear'、'exp'、'log'、'cos',也可使用 audio.op('fade').curves 中的函数。{start, end} 指定 0 到 1 的电平,可在任意电平之间淡变(音量闪避);{mid} 调整半振幅点的位置。≡ Audacity adjustable-fade |
.normalize(target?, mode?) | 去除直流偏移并标准化。响度目标会维持真峰值上限,默认为 -1 dBTP:先使用前瞻限制器,再补回为达到目标响度而降低的电平。预设符合 Apple Podcasts、Spotify、EBU R 128(ITU-R BS.1770-4):'podcast' -16 LUFS'spotify' -14 LUFS(YouTube、Tidal、Amazon Music 也适用)'broadcast' -23 LUFS-18, 'lufs' 任意响度;-3 峰值 dB;不带参数:峰值 0 dBFS;'rms' 模式传入一个音频实例:使用其综合响度 { ceiling: -2 } dBTP,false 表示关闭{ dc: false } 保留直流偏移{ adaptive: true } 用于直播,并立即开始:增益会根据已听到的内容调整,上限(峰值模式下为目标值本身)会保护尚未听到的部分。不启用时,整个选区使用同一增益:直播会等待结束后再处理。≡ FFmpeg loudnorm |
.roomtone(threshold?) | 用录音自身的房间底噪填充数字静音(低于 -90 dBFS 且持续 ≥ 10 ms:已剪掉的停顿、pad()):选取电平稳定持续 0.3 s、比节目音量低 20 dB 的安静片段。.trim().pad(1.5, 2).roomtone() 会在有声书章节的两端补上房间底噪(ACX 不接受数字静音)。如果所有停顿都经过噪声门处理或剪切,就没有可用的底噪片段,因此不会有任何变化。≡ iZotope RX Ambience Match |
.mix(source, at?, gain?) | 在 at 秒处叠加,源音量为 gain dB。≡ FFmpeg amix 权重 |
.crossfade(source, duration?, curve?) | 交叠后追加,默认 0.5s。'cos'(默认)适合相似素材;'equal'(等功率)可在不同音轨之间保持响度;曲线均可在 audio.op('crossfade').curves 中找到。不指定源时,.crossfade({ at, duration }) 会在该范围内交叉淡变,就像编辑器对选区进行交叉淡变一样:范围之前的音频淡入范围之后的音频,范围本身则被移除。≡ FFmpeg acrossfade |
.pan(value, opts?) | −1 表示左声道,0 表示居中,1 表示右声道。 |
.write(data, {at?}) | 从 at 处开始,用原始 PCM 或其他声音覆盖,如同磁带录制会覆盖已有内容;超出末尾的部分会延长音频。 |
.transform(fn) | 内联函数 (input, output, ctx) => void。不会序列化。 |
a.gain(-3) // reduce 3dB
a.gain(6, { at: 10, duration: 5 }) // boost range
a.gain(t => -12 * Math.cos(t * TAU)) // automate over time
a.fade(0.5, -2, 'exp') // 0.5s in, 2s exp fade-out
a.normalize('podcast') // -16 LUFS, -1 dBTP
a.normalize(-27, 'lufs', { ceiling: -2 }) // Netflix
a.mix(voice, { at: 2 }) // overlay at 2s
a.mix(bed, 0, -18) // music bed, 18 dB under
a.crossfade(next, 2) // 2s crossfade into next
a.crossfade(song2, 4, 'equal') // equal-power, for unrelated tracks
a.pan(-0.3, { at: 10, duration: 5 }) // pan left for range
滤波器
| 方法 | 描述 |
|---|---|
.highpass(freq, order?)、.lowpass(freq, order?) | Butterworth 通带滤波器;阶数为 ≥ 2 的偶数:2(12 dB/oct,默认值)、4(24)、6、8,……其他阶数将被拒绝。 |
.bandpass(freq, Q?)、.notch(freq, Q?) | 带通/陷波。 |
.allpass(freq, Q?) | 相移,幅度为 1。 |
.phase(angle?) | 将每个频率的相位转动 angle 度(希尔伯特变换;可为数字、曲线 {t, v} 或 t => degrees):幅度、RMS 和响度保持不变,转动 -angle 可还原。180 会逐个样本反转极性({ channel: 1 }:仅一个声道)。未设置时,会随时间选择能最大程度降低峰值的角度,同时处理所有声道,且绝不提高任何声道的峰值:使语音在零点两侧分布得更均匀;在 VoiceBank 的两个测试说话人(各 30 句)中,峰值平均降低 0.7 和 0.8 dB,最高可达 2.9 dB。若两个人轮流说话,则分别为每个人设置独立角度。流延迟 0.28 s。≡ iZotope RX Phase |
.lowshelf(freq, dB)、.highshelf(freq, dB) | 搁架均衡器。 |
.eq(freq, gain, Q?) | 参数均衡器。 |
.filter(type, ...params) | 按类型名称指定,或使用自定义滤波器函数。 |
全部为双二阶滤波器。
a.highpass(80).lowshelf(200, -3) // rumble + mud
a.eq(3000, 2, 1.5).highshelf(8000, 3) // presence + air
a.notch(50) // remove hum
a.allpass(1000) // phase shift at 1kHz
a.phase() // the peaks lowered, nothing heard
a.phase(180, { channel: 1 }) // the right channel's polarity
a.filter(customFn, { cutoff: 2000 }) // custom filter function
效果
| 方法 | 描述 |
|---|---|
.vocals(mode?, {model?}) | 中/侧声道:'isolate'(默认)保留中置声道,'remove' 保留侧声道。model 则改用训练好的模型进行分离,可选 'mel-roformer'(Kim 的 Mel-Band RoFormer,MIT 权重,仅人声,在支持 WebGPU 的环境中运行:MUSDB18 预览曲目的人声 SDR 为 12.08 dB,是此处最高的;在 M4 Max 上为 0.44 倍实时速度)、'scnet-large'(SCNet,MIT 权重)、'umxhq'(Open-Unmix,MIT 权重)或 'htdemucs'(Hybrid Transformer Demucs,仅限研究用途的权重),通过可选的 @audio/neural-separate 使用;此时 'remove' 会减去模型分离出的人声,因此两者逐采样相加可还原输入:人声与其余声音分别修复后再混合(在 Node 中,audio.memo = new Map() 可让模型只为两者运行一次;playground 中可将人声分离到新音轨)。'scnet-large' 和 'scnet' 由远程服务提供;其他权重需在本地导出(方法),或从 weights 提供。≡ SoX oops;SCNet、Demucs、Open-Unmix |
.rebalance(vocals?, bass?, drums?, other?, {model?, residual?, step?}) | 分别调节歌曲中的人声、贝斯、鼓和其他声音的电平,单位为 dB(−Infinity 表示静音):分离模型将输入拆分为四个音轨,再将每个音轨的变化量加回输入,因此每个音轨均为 0 dB 时,输入逐采样保持不变;模型未分配到任何音轨的内容也保持原样,或按 residual dB 处理(−Infinity:仅保留分离出的音轨,因此单独的音轨与减去该音轨后的输入逐采样相加可还原输入)。model 可选 'scnet-large'(默认;SCNet-large,MIT 权重,169 MB)、'scnet'(43 MB,耗时为三分之一)、'htdemucs'(仅限研究用途的权重)或 'umxhq',通过可选的 @audio/neural-separate 使用;其权重需在本地导出(方法),或从 weights 提供。输入只分离一次,按每隔 step 秒(0.5;0.25 会让每个采样被听取四次,耗时翻倍,精度提高 0.07 到 0.17 dB)处理 11 秒片段;调整增益后再读回音轨。MUSDB18 测试预览曲目中,各音轨单独播放时的 BSSEval v4 SDR,人声 · 贝斯 · 鼓 · 其他:10.75 · 8.17 · 10.30 · 6.94;iZotope RX 12 Music Rebalance 的最佳结果为 10.89 · 9.66 · 9.85 · 6.45;在 50 首歌曲中,分别有 33、32、38、32 首的结果更好;人声提高 6 dB 时为 20.2 dB,对比 19.5;鼓降低 6 dB 时为 21.9,对比 20.9(bench/rx/separate.mjs)。≡ iZotope RX Music Rebalance |
.scene(dialogue?, music?, effects?, {model?}) | 分别调节音轨中的对白、音乐和音效电平,单位为 dB(−Infinity 表示静音):分离模型将输入拆分为三个音轨,再将每个音轨的变化量加回输入,因此每个音轨均为 0 dB 时,输入逐采样保持不变。model 可选 'mrx'(默认;MRX,Petermann 等人,ICASSP 2022,MERL 的 MIT 权重,122 MB,速度为实时的五分之一)或 'tiger'(TIGER,ICLR 2025,Apache-2.0 权重,29 MB,耗时约为 50 倍:在 30 条测试片段上为 12.7 · 10.2 · 8.1 dB,而 MRX 为 10.9 · 5.2 · 5.7;对白提高 6 dB 时为 21.0,对比 18.7),通过可选的 @audio/neural-separate 使用;其权重需在本地导出(方法),或从 weights 提供;输入只分离一次,调整增益后再读回音轨。Divide and Remaster v3 的英语测试集包含 150 条、每条 60 秒的片段,SNR 中位数,对白 · 音乐 · 音效:11.8 · 5.3 · 6.0 dB(对白经 deepfilter(0) 处理后为 10.5;其作者的 Bandit v2,CC BY-SA 权重:15.6 · 10.4 · 9.9);对白提高 6 dB 时,较真实混音高出 18.9 dB,而保持原样时为 7.2(bench/rx/scene.mjs)。RX 12 Scene Rebalance 只能在 Pro Tools(AAX)中运行:未进行测量。≡ iZotope RX Scene Rebalance |
.dither(bits?, {shape?}) | TPDF,默认 16 位。shape: true 会添加二阶噪声整形:量化噪声移至约 Nyquist/2 以上,听感上更安静。 |
.crossfeed(freq?, level?) | 耳机串音,默认 700 Hz,0.3。 ≡ SoX earwax、bs2b |
.convolve(ir, mix?, {normalize?, tail?}) | 通过脉冲响应处理声音:例如房间、板式混响、音箱或录制好的麦克风响应。ir 可以是文件或 URL、音频实例,或采样通道;会重采样至声音的采样率;通道 c 经过 IR 的通道 c,循环使用。无延迟(Gardner 的分区卷积:直接处理前段,后续部分使用 FFT 分区),并在末尾之后渲染衰减尾音(tail: false 时不渲染)。mix 范围为 0 到 1(默认 1);normalize: true 会将 IR 归一化为单位能量。≡ Pedalboard Convolution、FFmpeg afir |
.resample(rate, {type?}) | 升采样默认使用线性插值,降采样默认使用抗混叠窗函数 sinc,滤波器抽头随比率增大。type: 'sinc' 或 'linear' 可强制指定其中一种。 |
.crossover(...freqs) | N 个分频点 → N+1 个频带 × 通道,按频带优先排列。四阶 Linkwitz-Riley;各频带相加后保持平直。 ≡ FFmpeg acrossover |
.match(ref, amount?) | 匹配 EQ:最多拟合 8 个参数均衡频带,以匹配参考与源频谱之比。只改变音色;响度由 normalize 保持。流式处理时落后 {lookahead} 秒(默认 10),并随着获取更多音频重新拟合。{ midside: true } 会分别匹配立体声对的中置声道和侧声道,并将侧声道电平匹配到参考的声场宽度。≡ iZotope Ozone Match EQ |
.align(ref, {within?, span?, drift?}) | 将音频在时间上对齐至参考音轨,以便同时播放:把在其他位置重制或单独录制的部分放回原位;如果较晚,就从头部裁切;如果较早,就在前面补静音。通过 GCC-PHAT 查找两者波形相同的采样点对应的延迟,否则根据起音查找(spectral flux);搜索范围为 ±within 秒(默认 10),读取前 span 秒(默认 60)。如果两者均持续至少 20 秒且延迟随时间增长(drift: false 可跳过此检查),则先调整采样率:将副本的播放时钟调至与参考相同,再逐采样读回,音高也随之变化;以其他速度演奏的部分(±10%)会在保持音高的同时拉伸至匹配,且在 bench/align.mjs 的大多数测试中,结尾误差不超过 2 ms。 |
.master(ref, opts?) | 以参考音轨为基准进行母带处理:在中置和侧声道上执行 match,然后将响度标准化至参考音轨的综合响度,并将真峰值控制在 -1 dBTP 以下({ ceiling })。≡ Matchering |
.auto(type?, {intensity?, targetLufs?, ceiling?}) | 根据音频内容修复并完成处理(@audio/chain):先进行测量,再根据每项问题的证据单独修复(declip、declick、deplosive、dehum、denoise、dereverb、deesser);只有测量结果表明确有需要时,才执行 EQ(如果人声频谱超出干净人声的范围,则将其调整到语音目标附近)、压缩粘合(响度范围超过 12 LU),并将响度调整至 type 指定的目标('speech',默认 −16 LUFS;'music' 为 −14;'voice-music')。真峰值会控制在 ceiling dB(默认 −1)以下,并在峰值需要时最多进行 6 dB × intensity 的真峰值限制。无需处理的音频除电平外会保持原样。在安装了 @audio/neural-denoise 时,语音下方的背景音会交给 DeepFilterNet3 处理(使用 deepfilter() 的模型,噪声降低 60 dB × intensity,并在配方中注明);否则使用 OM-LSA;'speech' 类型会保留语音。intensity 范围为 0–2(默认 1),用于缩放处理强度;为 0 时,将增益限制在峰值上限。对带有多个缺陷、但由干净录音制作的音频进行测试(bench/rx/assistant.mjs),其修复效果与调校过的 iZotope RX 12 Repair Assistant 对比:语音 PESQ 为 2.99,对比 2.56;SI-SDR 为 18.4 dB,对比 13.0;DNSMOS OVRL 为 3.12,对比 3.12;音乐 ODG 为 −1.69,对比 −2.23;干净语音为 4.46,对比 3.81;稳定背景音电平(PESQ 为 2.97,对比 2.94),RX 在 DEMAND 测试集上表现更好(2.53,对比 2.90:其中 9 条里有 4 条被判定为无背景音)。不安装神经网络软件包时,语音为 2.79,背景音为 2.39 和 2.07。完整运行 auto() 后,89% 的语音和 98% 的音乐与目标相差不超过 1 LU,真峰值不高于 −1.00 dBTP;限制器会使干净混音的 ODG 降至 0.30,而修复效果为 −0.22,输入为 0.21;干净人声不受影响(PESQ 4.45)。≡ iZotope RX Repair Assistant、Dolby.io Media Enhance |
.spectral(band?, gain?, {at, duration}) | 对时间 × 频率区域应用增益,band = [lo, hi] Hz;默认将其移除。≡ Audacity 频谱编辑、FFmpeg afftfilt |
.repair(band?, {at, duration, method?, window?}) | 根据受损范围(掉音、蜂鸣声、连续点击声)前后的音频重建该范围。method 为 'auto'(默认)时,会在前方 window 秒(默认 10)范围内搜索可无缝衔接的片段并移植;若找不到,则对最长 70 ms 的范围进行 AR 插值,更长的范围则使用正弦波桥接。'ar'、'sinusoidal'、'similarity'、'spectral' 可强制指定方法。≡ iZotope RX Spectral Repair |
.declick(threshold?, longest?, {at?, duration?}) | 移除杂音:黑胶唱片的爆音、剪辑不良的接缝、数字故障、语音中的口腔杂音。每个杂音与其周围的 AR 预测误差相比达到 threshold 倍(默认 8)时,才会被判定为杂音;其持续范围和是否伴有振铃,是判断声音特征的最佳依据。系统会根据其两侧各 46 ms 的音频重建最可能的值;如果是相距 2.5–15 ms、音色相似的脉冲(语音、拨弦)、声音本身的激励(爆破音、起音),或持续时间超过 longest ms(默认 6)的爆发声,则会视为正常声音并保留。指定 { at, duration } 后,只会查找该范围内的杂音,不会略过杂音来寻找相似片段或检查其持续时间。不会改变杂音影响范围以外的内容。流式处理时约落后 0.9 秒。将杂音放大至周围声音的 5 倍后:爆音降低 34 dB,故障降低 46 dB,口腔杂音降低 28 dB;在所有测试的类型和大小中,移除效果均优于 iZotope RX 12 De-click 和 Mouth De-click(详见 @audio/denoise-declick 的 README)。≡ iZotope RX De-click、Audacity Click Removal |
.denoise(reduction?, threshold?, {noise}) | 移除稳定的噪声(嘶声、嗡声和电流声、风扇声、房间底噪、磁带噪声),根据噪声单独出现的部分进行学习:noise 可以是该操作输入中的 { at, duration },也可以是多个此类范围,或通过 stat('print') 保存的噪声样本。噪声在所有位置降低 reduction dB(默认 12),也可只在操作自身的 { at, duration } 范围或 band [low, high] Hz 频带内降低,其余部分保持不变;保留下来的仍是相同噪声,只是更安静,不会产生音乐音调。threshold(dB)会抬高噪声样本的阈值:被视为噪声的低音量内容会更多。使用 OM-LSA 处理稳定噪声(@audio/denoise-omlsa),每个通道使用各自的噪声样本;实时源会在该范围到达后渲染。VoiceBank+DEMAND PESQ,以每位说话者开始前半秒的音频作为噪声样本:含噪输入为 1.97,omlsa() 为 2.40,denoise() 为 2.48。对于变化的噪声:使用 omlsa()、deepfilter()。≡ iZotope RX Spectral De-noise(Learn)、Adobe Audition Noise Reduction(噪声样本)、Audacity Noise Reduction |
.recover(cutoff?) | 根据人声自身的谐波重建 cutoff Hz 以上丢失的频带(例如电话线路、窄带编解码器、16 kHz 文件):通过 pYIN 估算音高,将截止频率以上的各次谐波合成至由截止频率以下的谐波确定的电平,每高一个八度降低 12 dB;谐波间的气声作为噪声处理,每高一个八度降低 2 dB。只在输入缺失该频带时添加,因此会衔接输入的滚降曲线,并保持截止频率以下的内容原样不变。如果未指定截止频率,则取长期频谱在三分之一倍频程内下降 25 dB 并保持在此水平的频率(没有人声会如此陡峭地滚降);若不存在,则不作任何更改。将 VoiceBank 测试集降至 8 和 16 kHz:重建频带上的对数频谱距离分别为 13.9 和 12.3 dB,sbr() 为 20.0 和 16.7,输入为 41.3 和 40.6(bench/recover.mjs);加入任何频带都会使 PESQ 下降(bench/recover.py),因此仍需进行听感测试来评估效果。≡ iZotope RX Spectral Recovery |
.deepfilter(limit?, {floor?, music?, weights?, device?})、.rnnoise(limit?) | 通过可选的 @audio/neural-denoise 进行神经网络语音降噪:也能去除变化的噪声(键盘声、交通声、繁忙房间里的噪声)。deepfilter 使用 DeepFilterNet3,在渲染前对整个输入进行处理;其 8 MB 模型只下载一次。rnnoise 使用 RNNoise 流式处理,权重包含在软件包中,延迟 30 ms。limit 表示噪声降低的幅度,单位为 dB:deepfilter 的默认值 18 是在语音本身开始听起来像经过滤波处理之前的最大值(DNSMOS SIG 在 18 时保持稳定,超过后会下降),因此会保留房间底噪;低于语音 floor dB(默认 40)的噪声会被降低至该下限,因此与语音一样响的噪声会被彻底去除,而非仅降低至语音以下 18 dB。rnnoise 的默认值 16 是在开始损伤语音之前的最大值:即使完全没有噪声,RNNoise 也会降低词尾音量(为 16 时降低 10 dB,无限制时降低 31 dB);0 会将其移除。deepfilter 会保留持续的歌声音符,而模型本身会将其误认为噪声(VocalSet:仅降低 1.9 dB,而非 39 dB),也能识别 16 kHz 文件或编解码器的空白高频段,避免将其当作模型训练时的噪声底(16 kHz VoiceBank+DEMAND PESQ 为 2.83,而非 2.72)。VoiceBank+DEMAND PESQ:含噪输入为 1.97,wiener() 为 2.19,rnnoise() 为 2.49,deepfilter() 为 3.05,deepfilter(0) 为 3.15,iZotope RX 12 Dialogue Isolate 为 2.72;在语音以下 5 至 −5 dB 的噪声、嘈杂人声和音乐背景音,以及房间噪声中,deepfilter() 为 1.99,而 RX 为 1.86(bench/rx/isolate.mjs)。单独的音乐会原样通过(music: 'enhance' 也会增强音乐)。≡ DeepFilterNet、RNNoise、iZotope RX Dialogue Isolate |
.dereverb(reduction?, {music?, method?, weights?, device?}) | 去除人声中的房间混响,包括早期反射和混响尾音;通过可选的 @audio/neural-separate 使用训练模型:anvuew 的房间去混响模型,是一个单声道 Band-Split RoFormer,使用 pyroomacoustics 模拟房间中的人声进行训练(GPL-3.0 权重,60 MB,只下载一次),在渲染前处理整个输入,每个通道分别处理。混响降低 reduction dB(默认 60:完全去除;12:保留一丝混响),然后混合回模型去除的内容,因此调整参数时无需再次运行模型。对模型而言,所有非人声内容都属于混响,因此音乐会原样通过(music: 'pass' 使用 @audio/neural-denoise 的语音/音乐保护机制,与 deepfilter 一样,需结合原始音频和模型提取的人声进行判断:长混响会使人声像持续音符一样共振);人声背景中的音乐会被视为语音,并与混响一同去除。使用模型从未听过的 19 间 MIT IR Survey 房间中的人声进行测试(bench/rx/dereverb.mjs,测试集;T60 为 0.38–1.85 秒):PESQ 从 1.39 提高至 2.65,STOI 从 0.77 提高至 0.94,尾音降低 15.6 dB(长片段从 1.37 提高至 2.58,降低 17.5 dB);调校后的 iZotope RX 12 De-reverb 为 1.45,关闭混响功能的 Dialogue Isolate 为 1.79。噪声比人声低 10 dB 时,从 1.26 提高至 1.70(RX Dialogue Isolate 为 1.33);干声从 4.64 降至 4.55,其 125 Hz 八度频带降低 2.1 dB。经过保护机制处理的音乐在所有八度频带中均恢复至与原始音频相差不超过 0.9 dB(MUSDB18 预览、GuitarSet 和四段音乐:原声、剪辑成片段的声音、房间中的声音);模型和保护机制都会将房间中的歌声音调视为人声,某个八度频带最多会降低 6 dB。method: 'wpe' 无需模型:使用加权预测误差和晚期混响增益(@audio/denoise-dereverb;strength),晚期混响尾音约降低 6 dB(PESQ 为 1.53);无间隙或无音乐的干声音频不会改变。≡ iZotope RX De-reverb、Dialogue Isolate 的混响处理 |
.vocos({crossover?, weights?, device?}) | 使用 Vocos 神经声码器重新生成语音(Siuzdak,ICLR 2024;charactr 的 MIT 权重),通过可选的 @audio/neural-vocode 使用:对其梅尔频谱的每个 STFT 帧重新预测幅度和相位,因此其他声码器留下的残响(例如 TTS 帧持续共振,或 10、20、30 ms 后的微弱副本)会被 Vocos 自身的声音取代。Vocos 无法生成 12 kHz 以上的声音:输入中高于 crossover Hz(默认 11000)的部分会通过 Linkwitz-Riley 分频保留;设为 0 时只保留 Vocos 的输出。其 27 MB 模型只下载一次。这是一款语音声码器:音乐经过处理后会发生变化 |
.derustle(reduction?, {ambience?}) | 去除领夹麦克风录音中衣物摩擦声,同时保留房间稳定的底噪:使用 deepfilter 的模型(对同一输入共享模型运行),并像 deepfilter 混合模型去除的内容一样将其混回;衣物摩擦声降低至比人声低 reduction dB(默认 45),房间底噪降低 12 dB(ambience: false 会将其全部去除)。参数根据 10 至 37 秒的说话者及衣物摩擦声进行选择(Freesound 的 CC0 和 CC BY 衣物音效与麦克风操作录音:没有开放许可的领夹麦克风摩擦声)。测试集包含 55 位说话者,摩擦声与人声一样响:PESQ 从 1.21 提高至 2.13,SI-SDR 从 −0.3 提高至 13.5 dB;只有房间底噪时为 4.37,干净录音为 4.61(deepfilter() 分别为 2.12、4.31、4.59;iZotope RX 12 Dialogue Isolate,即 RX 可托管的对白分离器,分别为 2.06、2.96、3.96)(bench/rx/derustle.mjs)。RX 12 De-rustle 只能在 Pro Tools(AAX)中运行:未进行测量。≡ iZotope RX De-rustle |
.deconstruct(tonal?, noise?, transient?, {separation?}) | 分别调节音调、噪声和瞬态部分的电平,单位为 dB:使用沿时间和频率方向的中值滤波器(Fitzgerald 2010),并通过 separation(默认 2;Driedger、Müller 与 Disch 2014)将既非音调也非瞬态的部分视为噪声;各通道共享软掩码。各部分相加后可还原输入;每部分均为 0 dB 时,输入逐采样保持不变。和弦 100% 归入音调部分,点击声归入瞬态部分,嘶声 68% 归入噪声部分(在另外两部分中各占 1%)。≡ iZotope RX Deconstruct |
.azimuth(delay?) | 将立体声对在时间和极性上对齐,使第二个通道与第一个通道对齐。未设置时,每秒测量一次延迟(使用 Knapp 与 Carter 于 1976 年提出的广义互相关加权方法,并在采样点之间进行细化:精确的分数采样移位可还原至 0.0001 个采样),并跟踪延迟变化;接线反接的通道会被反转;已对齐的立体声对(例如混音的立体声像)保持逐采样不变。delay 以毫秒为单位手动设置延迟。≡ iZotope RX Azimuth |
.dewow(mode?) | 校正抖晃和颤动:测量唱片或磁带随时间变化的转速,再按其倒数读取声音;可根据音乐本身的音符(唱片每转一次的抖晃,'partial')、导频或校准音('reference',refFreq),或单个人声的音高('pitch')进行校正。若无证据证明存在抖晃,则不作任何更改:没有抖晃的录音会逐采样还原(@audio/denoise-dewow)。≡ iZotope RX Wow & Flutter |
.desqueak(squeak?, {pick?, amp?}) | 降低吉他弦的摩擦声,即手指在缠弦上滑动时产生的声音。通过音符不具备的特征与音符区分:识别在 93 ms 帧上保持频点不变的泛音(拨弦产生的低次泛音或点击声除外);持续至少 45 ms 时,将每个频点降低至摩擦声周围的水平,最多降低 squeak dB(默认 30);摩擦声下方持续共振的音符会保留其泛音,摩擦声以外的音频逐采样保持不变。pick dB 会将每次拨弦的点击声降低至音符在 15 ms 后共振的水平(设为 -9 可还原增强 6–12 dB 的起音);amp dB 会降低稳定的嘶声和电流声(使用最安静帧的噪声样本进行 OM-LSA 处理,并保留音符的峰值)。GuitarSet 测试曲目与 iZotope RX 12 Guitar De-noise 对比:添加摩擦声后,摩擦声误差降低 3.3 dB,而对方为 2.7;SNR 为 23.2 dB,而对方为 20.5;干净录音中 2.4% 的采样被改变,而对方为 12.9%;刺耳拨弦声降低 8.9 dB,而对方调校后为 3.3;录制时的摩擦声中位数降低 12.8 dB,而对方为 13.5;它只会处理 RX 所处理的拨弦声和点击声的一半(@audio/denoise-desqueak、bench/rx/guitar.mjs)。≡ iZotope RX Guitar De-noise |
.codec(format?, bitrate?, {quality?}) | 还原有损编解码器处理后的声音:在 bitrate kbps(默认 128)下选择 'mp3'(默认)、'aac'、'opus' 或 'vorbis';'mp3' 可使用 quality(LAME 的 -V:0 为最佳,小于 10)进行 VBR 编码;'gsm' 使用 GSM 06.10 全速率编码,13 kbps、8 kHz,模拟 20 世纪 90 年代的电话线路;在原位进行编码和解码。会消除每种编解码器自身的延迟(使用其无缝衔接信息,再通过相关性验证),因此与输入的对齐误差不超过 0.03 个采样(在 44.1 和 48 kHz 下):可进行 A/B 对比,或试听编解码器移除了哪些声音。编解码器会改变峰值:标准化至 -14 LUFS、低于 -1 dBTP 的母带,经过 160 kbps Opus 处理后峰值最多高出 0.5 dB,经过 Vorbis 处理后高出 0.3 dB,经过 320 kbps MP3 和 256 kbps AAC 处理后则低于 0.1 dB(8 首 MUSDB18 混音的 7 秒片段);.codec('opus', 160).stat('truepeak') 可测量真峰值。≡ iZotope RX Streaming Preview、Pedalboard MP3Compressor、GSMFullRateCompressor |
a.vocals() // isolate center-panned vocals
a.vocals('remove') // remove vocals (karaoke)
a.vocals({ model: 'umxhq' }) // vocals by a separation model
a.rebalance({ vocals: 6, drums: -3 }) // the vocals up, the drums down
a.rebalance(-Infinity) // a karaoke track
a.scene(6) // a film's dialogue 6 dB up over its music and effects
a.scene(-Infinity) // a music and effects track
a.dither(16) // TPDF dither to 16-bit
a.dither(16, { shape: true }) // noise-shaped
a.crossfeed() // headphone crossfeed
a.convolve('hall.wav', 0.3) // 30 % of a captured hall
a.resample(48000) // resample to 48kHz (linear)
a.resample(96000, { type: 'sinc' }) // high-quality windowed-sinc
a.match(reference, 0.7) // 70% of the way to its tone
a.align(original) // a part remade elsewhere, put back where the original stood
a.spectral([1000, 4000], -30, { at: 2.1, duration: 0.3 }) // a cough
a.repair({ at: 1.2, duration: 0.05 }) // a dropout
a.repair({ at: 42, duration: 1 }) // a lost second of music: the passage that fits
a.declick() // every click
a.declick({ at: 12.31, duration: 0.02 }) // the clicks seen there
a.denoise({ noise: { at: 1.2, duration: 0.5 } }) // hiss learned from a pause, 12 dB down everywhere
a.deepfilter() // speech out of noise: room tone 18 dB down and kept, loud noise to 40 under the voice
a.rnnoise() // the same, streaming
a.dereverb() // the room off a voice
a.dereverb(12) // the room 12 dB down
a.derustle() // a clip-on mic's clothing rustle, the room kept
a.vocos() // a TTS voice made again by a neural vocoder, its frame echo gone
a.deconstruct(0, -12) // the hiss 12 dB down, tones and attacks as they were
a.azimuth() // a tape's two tracks back in line
a.dewow({ mode: 'reference', refFreq: 1000 }) // a transfer's wow, read from its calibration tone
a.desqueak() // a guitar's string squeaks
a.desqueak({ pick: -9, amp: -20 }) // and its harsh picks, and the amp's hiss and buzz
a.codec('aac', 256) // what a 256 kbps AAC stream does to it
a.codec('gsm') // a phone call
输入/输出
| 方法 | 说明 |
|---|---|
await .read(opts?) | 渲染后的 PCM。使用 { format, channel } 转换。对于仍在传输的数据,会等待:读取一个范围时等到该范围传输完成,读取全部数据时等到结束(对于无限流:读取范围,或使用 stream())。 |
await .save(path, opts?) | 编码并写入,格式根据扩展名确定。无损格式会保留源深度;{ bitDepth, bitrate, quality, codec } 用于设置编码器;m4a 和 mp3 会将标记写为章节。输出在编码时流式写入,结束时会用总量修补头部(管道会将其保留为“未知”);来自实时源的 m4a 会采用分片格式。将视频源保存为 .mp4/.mov 时会保留画面,只更改音轨。.edl、.otio、.fcpxml 会写入剪辑列表。 |
await .encode(format?, opts?) | 编码为 Uint8Array;'edl'、'otio'、'fcpxml':将剪辑列表编码为 UTF-8。 |
await .cuts(format?, opts?) | 将编辑内容生成为视频编辑器可用的剪辑列表:'edl'(CMX 3600:Premiere、Resolve、Avid)、'fcpxml'(Final Cut Pro、Resolve)、'otio'(OpenTimelineIO);不指定格式时返回 { fps, clips: [{ at, duration, from, rate, source }] }。剪辑、移动、间隙和插入的文件用于定位片段;速度、拉伸和变速会设置其速率;标记也会一并包含。处理不会包含在列表中:将处理后的音频铺在画面下方。剪辑会对齐视频轨道的帧(MP4/MOV),否则使用 { fps }(30),与声音的偏差不超过半帧;其时间码轨道会以源时间为起点计数,并根据计数方式使用丢帧时间码({ dropFrame })。CLI 的 save cuts.edl 命令会写入一个剪辑列表。 |
.clone() | 编辑相互独立,页面共享。 |
.push(data, format?) | 将 PCM 输入可推送实例;.stop() 会完成处理。 |
let pcm = await a.read() // Float32Array[]
let raw = await a.read({ format: 'int16', channel: 0 })
for await (let block of a) send(block) // async-iterable over blocks
await a.save('out.mp3') // format from extension
await a.save('book.mp3', { bitrate: 192 }) // ACX: 192 kbps CBR
await a.save('master.wav', { bitDepth: 24 }) // 24-bit
await a.save('talk.mp4') // video in, video out
let bytes = await a.encode('flac') // Uint8Array
let b = a.clone() // independent copy, shared pages
let src = audio() // pushable source
src.push(buf, 'int16') // feed PCM
src.stop() // finalize
播放/录音
| 方法 | 说明 |
|---|---|
.play(opts?) | { at, duration, loop, volume, rate, paused, device }。at 默认为 currentTime(结束后则默认为起点);如果已在播放,会无缝跳转到该位置。device:按 ID 或名称指定输出设备(页面会在专用的 AudioContext 上播放,并将音频输出到该设备)。 |
.play({ from: b }) | 接管 b 当前所在位置的播放状态(其范围、循环、音量、速率和暂停状态),并进行交叉淡化,不留间隙;b 会停止。 |
.pause()、.resume()、.seek(t)、.stop() | 每项操作都会在 5 ms 内渐变,不会产生爆音;seek 会进行交叉淡化,并在循环中保持在其范围内。.stop() 也会结束录音。 |
.record(opts?) | 麦克风。{ device, sampleRate, channels, monitor }。device:按 ID 或名称指定输入设备。monitor:将录音通过当前编辑内容实时送至默认输出(true),或送至按 ID 或名称指定的输出,延迟约 50 ms(输入读取、一个数据块、输出环形缓冲区,以及编辑本身的前瞻处理所需时间);这相当于 DAW 中通过音轨插入效果器进行监听。≡ Pedalboard AudioStream |
audio.devices() | 输入和输出设备:{ input: [{ id, name, default }], output: [...] }。Node:平台自身的 ID(CoreAudio 的 UID、WASAPI 的端点 ID、ALSA 的名称);页面:enumerateDevices(),获准使用麦克风后会显示名称。 |
audio.context | 页面唯一的 AudioContext,播放时会使用它:首次使用时创建,由首次手势触发恢复;播放前可设置为你自己的 AudioContext。 |
播放会将最多 2 s 的音频提前渲染到 audio.context 上的 AudioWorklet 中(Node:@audio/speaker),因此繁忙的主线程不会导致播放中断,且 play() 后数毫秒内即可听到声音(设备自身的延迟除外)。对正在播放的实例进行编辑后,约 50 ms 后会在相应位置听到变化:提前渲染的音频会平滑切换,并进行交叉淡化。对于仍在传输的数据源(正在解码或已推送),会先播放已收到的部分,后续数据到达后继续播放。设备会按原样播放任意声道数,并自行进行混音。
a.play({ at: 30, duration: 10 }) // play 30s–40s
await a.played // wait for sound
a.volume = 0.5; a.loop = true // live adjustments
a.muted = true // mute without changing volume
a.playbackRate = 1.5 // faster, the pitch kept
a.preservesPitch = false // the pitch follows the speed, as a tape's
a.pause(); a.seek(60); a.resume() // jump to 1:00
a.highpass(80) // an edit while playing: heard where it happens
b.play({ from: a }) // b takes over at the same place, crossfaded
b.stop() // end playback or recording
await audio.context.audioWorklet.addModule('./scrub.js') // your own nodes, on the same context
let scrub = new AudioWorkletNode(audio.context, 'scrub')
let mic = audio()
mic.record({ sampleRate: 16000, channels: 1 })
mic.stop()
await audio.devices() // { input: [{ id, name, default }, …], output: […] }
let take = audio()
take.compressor().plate(0.2) // edits, heard as they come in
take.record({ device: 'USB', monitor: 'Headphones' }) // by id, name, or part of a name
计量
| 方法 | 描述 |
|---|---|
.meter(what, cb?) | 实时获取每个音频块中播放内容的统计数据,并在播放时传递:rms、peak、ms、min、max、dc、clipping、spectrum,或自定义统计项。不传入 cb 时,可读取 .value。返回 { value, stop() }。工作线程门面也适用:在工作线程中测量,在页面上接收。 |
| 选项 | 描述 |
|---|---|
type | 统计项名称、名称数组,或省略以获取所有音频块统计数据。 |
channel | 单个声道使用 n,按声道分别统计使用 [n, m],省略则返回标量平均值(与 a.stat() 一致)。 |
smoothing | 单极 EMA 时间常数 τ,单位为秒。 |
hold | 峰值保持衰减 τ,单位为秒。 |
bins, fMin, fMax | 频谱分辨率和范围(当 type: 'spectrum' 时)。 |
a.meter('rms', v => draw(v)) // scalar avg across channels
a.meter(['rms', 'peak'], v => draw(v)) // { rms, peak }
a.meter({ type: 'rms', channel: [0, 1] }, v => draw(v)) // [L, R]
a.meter({ type: 'spectrum', bins: 64, smoothing: 0.15 }, drawFFT) // Float32Array of mel bins
a.meter({}, ({ delta, offset }) => draw(delta)) // no type → all block stats
let m = a.meter({ type: 'rms' }) // pull form
requestAnimationFrame(function tick() { draw(m.value); requestAnimationFrame(tick) })
m.stop() // release
分析
| 方法 | 描述 |
|---|---|
await .stat(name, opts?) | 获取单个值;使用 { bins: n } 时返回 Float32Array,其中包含范围内 n 个区段各自的值:不仅有多少,还有位置(列表、音调和频谱会整体返回;bins 用于设置频谱和倒谱的大小);传入名称数组则返回数组。{ channel: n } 表示单个声道,[n, m] 表示按声道分别统计;{at, duration} 表示子范围。 |
await .detect(opts?) | 单次扫描后返回 { bpm, confidence, beats, onsets };{ channel } 的用法与 stat 相同。 |
await .check(spec) | 根据交付规范判断通过或失败:{ pass, rules: [{ name, value, unit, min, max, pass }] }。'acx'(RMS、峰值、噪声底、室内音、44.1 kHz)、'podcast'(Apple:-16 LUFS ±1,≤ -1 dBTP)、'spotify'(播放时为 -14 LUFS,≤ -1 dBTP)、'broadcast'(EBU R 128:-23 ±0.2 LUFS,≤ -1 dBTP)、'netflix'(对白 -27 ±2 LUFS,≤ -2 dBTP)。每个限制均在 fn/check.js 中注明来源。 |
| 统计项 | 描述 |
| :-- | :-- |
'db' | 峰值振幅,单位为 dBFS。 |
'rms' | RMS 振幅,线性值(CLI 输出 dBFS)。 |
'noisefloor' | 最安静的 0.4 s 的 RMS,单位为 dB:词语之间的房间底噪(ACX Check 的测量值,精确到采样点)。 |
'print' | 某个范围的噪声印记,格式与 denoise({ noise }) 接受的格式相同:1025 个频带的 dB 值,频带间隔为 23.4375 Hz,范围为 0 至 24 kHz(RMS 为 0.01 的白噪声会生成 −40 的印记)。 |
'peak' | max(|min|, |max|),线性值。 |
'loudness' | 综合响度,单位为 LUFS(ITU-R BS.1770-4;对环绕声道加权,不包括 LFE)。 |
'momentary'、'shortterm' | 最大 400 ms / 3 s 响度,单位为 LUFS(EBU Tech 3341)。 |
'dialog' | 仅语音的响度,单位为 LUFS:自动检测语音(AES TD1008 对话响度)。 |
'dc' | 直流偏移。 |
'clipping' | 达到或超过 16 位满量程(±32767/32768)的削波采样点(标量:时间戳;分箱:计数)。 |
'silence' | 静音范围,格式为 {at, duration}。 |
'crest' | 峰值/RMS,单位为 dB。正弦波约为 3 dB,方波约为 0 dB。 |
'centroid' | 频谱质心,单位为 Hz(明亮度)。 |
'flatness' | 频谱平坦度:0 表示音调,1 表示噪声。 |
'correlation' | 左/右声道相位相关性,范围为 −1 至 +1。单声道返回 1。 |
'max'、'min' | 波形每个分箱的峰值包络。 |
'spectrum' | 以 dB 表示的梅尔频谱(A 加权);多个声道时,取其平均功率。 |
'cepstrum' | MFCC。 |
'bpm' | 节奏。 |
'beats'、'onsets' | 时间戳,类型为 Float64Array(秒)。 |
'hits' | 电平跳变的位置,向上(敲击)或向下(停止),类型为 Float64Array(秒):每个时间点位于起音的零交叉处,即剪切位置;而 'onsets' 按 23 ms 的块读取。 |
'notes' | [{time, duration, freq, midi, note, clarity}](pYIN + Tony 音符 HMM);设置 robust: true 时,即使存在噪声和房间声也能得到相同结果(神经网络 pYIN 第一阶段);设置 poly: true 时,返回复音 [{time, duration, freq, midi, note, velocity, bends}](Basic Pitch)。 |
'chords' | [{time, duration, label, root, quality, bass, confidence}]:在 NNLS 色度上运行 Chordino(Mauch & Dixon 2010),并与参考插件匹配;标签如 'Am'、'G7'、'C/E'、'N'。 |
'key' | {tonic, mode, label, confidence}(Krumhansl-Schmuckler)。 |
'voicing' | 音域中有声部分的占比,范围为 0 至 1:pYIN 音高曲线中有音高的帧,每 10 ms 取一帧。 |
'hnr' | 有声帧的谐波噪声比,单位为 dB(Boersma 1993;逐帧匹配 Praat 的 To Harmonicity (ac));没有有声帧时为 null。 |
'harmonic' | 周期成分的电平,单位为 dB:每帧功率中的周期部分占比(Boersma 的 r)乘以功率。与 RMS 不同,它不会因去除噪声而改变:表示编辑后保留下来的语音。 |
'similar' | 音频中其他相似的位置,例如咳嗽声、咔嗒声或哔声:[{ at, duration, score }]。提取该范围的对数梅尔频谱块,将每个频带除以其中位数,再通过皮尔逊相关系数沿整个音频滑动比较,阈值由 threshold 指定(0.7);band [low, high] Hz 仅比较这些频率。另有 a.similar(range, opts)。≡ iZotope RX Find Similar |
选项:bpm、beats、onsets 接受 { minBpm, maxBpm, delta, frameSize, hopSize };notes 接受 { minFreq, maxFreq, frameSize, hopSize, minDuration };chords、key 接受 { frameSize, hopSize, tuning }(在 44.1 kHz 下,帧长为 16384 个采样点;其他采样率下为 0.34 至 0.51 秒,每帧的八分之一;除非指定以 Hz 为单位的 tuning,否则从音频读取标准音 A);chords 还接受 boostN(无和弦偏置,0.1);key 还接受 `method: 'nnls' | 'pcp'。chords需要@audio/mir-nnls-chroma和@audio/mir-chordino,key需要@audio/mir-nnls-chroma:这些是参考插件的 GPL-2.0-or-later 许可译本,可按需安装(npm i @audio/mir-nnls-chroma @audio/mir-chordino);key使用method: 'pcp'时只需要 MIT 许可的@audio/mir-chroma和@audio/mir-key,除非跳过可选依赖,否则会随 audio 一起安装。notes使用robust: true时需要@audio/neural-pitch(权重包含在内):用网络的音高候选替代 YIN 的候选,在 YIN 漏掉音符处保留音符(Vocadito 起音检测在 0 dB SNR 下为 F 0.76,对比 0.53),但在干净音频上的表现略逊,因此默认仍使用 YIN。notes使用poly: true时接受{ minFreq, maxFreq, minDuration, onsetThreshold, frameThreshold },并需要 @audio/neural-transcribe,其模型会在首次使用时下载;bends` 表示每 11.6 ms 一帧相对于音符音高的音分,步进为 33.3 音分(音准正确的音符读数为 0)。 |
let loud = await a.stat('loudness') // LUFS
let [db, clips] = await a.stat(['db', 'clipping']) // multiple at once
let spec = await a.stat('spectrum', { bins: 128 }) // frequency bins
let [min, max] = await a.stat(['min', 'max'], { bins: 800 }) // peak envelope for canvas rendering
await a.stat('rms', { channel: 0 }) // left only → number
await a.stat('rms', { channel: [0, 1] }) // per-channel → [n, n]
let gaps = await a.stat('silence', { threshold: -40 }) // [{at, duration}, ...]
let bpm = await a.stat('bpm') // 120.5
let beats = await a.stat('beats') // Float64Array [0, 0.5, 1, ...]
let { bpm, confidence, beats, onsets } = await a.detect() // full pipeline, one pass
let notes = await a.stat('notes') // [{time, duration, freq, midi, note: 'A4', clarity}]
let chords = await a.stat('chords') // [{time, duration, label: 'Am', confidence}]
let k = await a.stat('key') // {label: 'C', mode: 'major', confidence}
let coughs = await a.similar({ at: 12.3, duration: 0.4 }) // [{ at, duration, score }]: the other coughs
元数据
| 属性 | 描述 |
|---|---|
.meta | 标签:{title, artist, album, year, bpm, key, comment, pictures, raw, ...}。可写入。meta.raw 保留未经修改的格式专属数据块(WAV bext/iXML、ID3v2 帧、FLAC 数据块)。 |
.meta.pictures | 封面图 [{mime, type, description, data, url}]。.url 是惰性 Blob URL(浏览器)或数据 URL(Node)。 |
.markers | 输出秒数中的 [{time, label}];编辑会移动或删除它们。 |
.mark(time, label?) | 在 time 处添加标记,单位为秒,表示截至当前编辑后的音频位置;{at, duration} 表示一个区域。后续编辑会保留标记;位于静音中时,标记会保持在离其最近声音的距离;超出末尾时,标记停留在末尾,直到后续编辑使音频延伸至该位置。可链式调用。 |
.regions | [{at, duration, label}];编辑会移动或删除它们。 |
解码时解析,保存时写入;支持往返处理 WAV、MP3、FLAC。
let a = await audio('song.mp3')
a.meta.title // 'Track Name'
a.meta.artist = 'Me' // mutate
img.src = a.meta.pictures[0].url // lazy Blob URL
a.crop({ at: 10, duration: 30 })
a.markers // re-projected — outside markers dropped, inside shifted
await a.save('edited.mp3') // tags + pictures preserved
await a.save('stripped.wav', { meta: false }) // opt out
工具函数
| 方法 | 描述 |
|---|---|
.on(event, fn)、.off(event?, fn?) | 订阅/取消订阅。 |
.undo(n?) | 返回撤销的编辑,可通过 .run() 重做。 |
.run(...edits) | 应用 ['type', opts] 编辑:操作参数(value、freq 等)以及范围键。 |
.dispose() | 释放资源。支持 using。 |
| 事件 | 描述 |
|---|---|
'data' | 解码/推送页面。载荷:{ delta, offset, sampleRate, channels }。 |
'change' | 任何编辑或撤销操作。 |
'metadata' | 流头已解码。载荷:{ sampleRate, channels, estDuration }:音频时长(秒),取自头部声明的时长(WAV、AIFF、FLAC、MP3 的 Xing 或 VBRI 帧、恒定比特率),否则根据文件大小计算;两者都未知时为 null。 |
'timeupdate' | 播放位置,按听到的进度更新(每秒约 50 次)。载荷:currentTime。 |
'play' | 播放已开始或恢复。 |
'pause' | 播放已暂停。 |
'volumechange' | 音量或静音状态已更改。 |
'ended' | 播放已结束:到达末尾、调用 stop(),或被 play({ from }) 接管时触发;循环播放时不触发。 |
'progress' | 保存/编码期间触发。载荷:以秒为单位的 { offset, total }。 |
a.on('data', ({ delta }) => draw(delta)) // decode progress
a.on('timeupdate', t => ui.update(t)) // playback position
a.run(
['gain', { value: -3, at: 10, duration: 5 }],
['crop', { at: 1, duration: 2 }],
['fade', { in: 1, curve: 'exp' }],
['insert', { source: ref, at: 2 }],
)
a.undo() // undo last edit
b.run(...a.edits) // replay onto another file
JSON.stringify(a); audio(json) // serialize / restore
插件
| 方法 | 描述 |
|---|---|
audio.use(...plugins) | 注册 @audio contract 工厂、一个统计项 { stat, compute }、一个编解码器 { codec, test?, decode?, encode? }、接收 audio 的函数,或一个 注册表 名称。注册表插件无需调用 use:首次使用时会加载 a.compressor() 和 a.stat('truepeak')。 |
audio.op(name, descriptor) | 注册一个 op:process 函数,或 { params, process, plan, resolve }。 |
audio.op(name?) | 返回一个描述符,或所有 ops。 |
audio.stat(name, descriptor) | 注册一个统计项:(chs, ctx) => [...] 或 { block, reduce, query }。 |
.plugin(ref, opts?) | 将原生插件作为编辑操作:通过可选的 @audio/host(Node),按文件、名称或 ID 加载 VST3、CLAP、Audio Unit 或 LV2 插件。参数可按键名使用插件自身的单位,也可用函数或 { t, v } 自动化;会扣除其延迟,并渲染其尾音(tail 秒数,false 表示无);通过 notes、midi、bpm、timeSignature 指定其接收内容;key 为侧链;preset、state;plugin 用于从包含多个插件的文件中选择一个;params 用于指定名称与选项相同的参数(mix、at);isolate 使其在独立进程中运行。audio --plugins 列出插件,audio --plugins NAME 列出某个插件的参数。也可在页面中通过模块 URL 或类加载 Web Audio Module(WAM 2.0):由 WAM SDK 在 OfflineAudioContext 中托管(Node:web-audio-api),播放前将输入音频渲染至其中,扣除补偿延迟,并渲染其衰减直至静音;参数可按 ID 或标签指定,自动化、notes、midi 和 bpm 作为 WAM 事件传入。≡ Pedalboard 的 load_plugin、DAW 的插入效果器、WAM 主机 |
插件也可在引擎之外运行:audio/batch 处理完整信号,audio/stream 处理实时分块。插件教程。
import { compressor } from '@audio/dynamics-compressor/audio'
audio.use(compressor) // bring-your-own factory
a.freeverb({ room: 0.8 }) // registry plugin: loads on first render; tail composes
music.ducker({ key: voice }) // sidechain via the key option
await a.stat('truepeak') // stat plugins land on a.stat()
audio.op('crush', { params: ['bits'], process: (input, output, ctx) => {
let steps = 2 ** (ctx.bits ?? 8)
for (let c = 0; c < input.length; c++)
for (let i = 0; i < input[c].length; i++)
output[c][i] = Math.round(input[c][i] * steps) / steps
}})
a.crush(4) // custom op, chainable like built-ins
a.plugin('RX 12 De-click', { sensitivity: 6 }) // native plugins: npm i @audio/host
a.plugin('AUDelay', { delayTime: 0.25, feedback: t => 20 * t }) // automated, on the timeline
audio(4).plugin('Surge XT', { notes: [{ time: 0, duration: 1, note: 'C4' }], bpm: 96 })
Worker
| 调用 | 描述 |
|---|---|
audioWorker(source, opts?) | API 相同,引擎运行在 Worker 中;主线程只保留几 KB 的外观对象。 |
audio(source, { worker: true }) | API 相同,需先导入 audio/worker。 |
{ worker: new Worker(url) } | 使用你自己的 Worker 入口:编解码器、插件、自定义代码和消息,然后加载 audio/worker,它会通过自己的端口通信。 |
expose(a) → id,audioWorker.adopt(id, { worker }) | 将你的 Worker 创建的实例交给页面,作为外观对象使用。 |
audioWorker.context | 页面的 AudioContext,与 audio.context 相同。 |
跨越边界时,clip()、split()、clone() 会返回 Promise;op 错误会触发 'error';函数无法跨越边界,请使用 {t, v} 曲线。play() 会在 Worker 中渲染,并直接传入页面的 AudioWorklet:即使主线程停顿数秒,也不会出现音频中断。架构。
import audioWorker from 'audio/worker'
let a = audioWorker('track.mp3') // decode/edits/stats/encode in a Worker
a.gain(-3).fade(0.5)
let [mins, maxs] = await a.stat(['min','max'], { bins: 640 }) // transferred, zero-copy
a.play() // rendered in the worker, played by an AudioWorklet (Node: @audio/speaker)
// your own worker: its messages stay its own, and what it makes plays on the page
import audio from 'audio' // worker.js
import { expose } from 'audio/worker'
self.onmessage = ({ data }) => self.postMessage({ out: expose(audio(data.file).gain(-3)) })
let worker = new Worker('./worker.js', { type: 'module' }) // page
worker.onmessage = ({ data }) => audioWorker.adopt(data.out, { worker }).play()
CLI
npm i -g audio,也可不安装,直接运行:npx audio …
audio [source] [transforms...] [sink] [options]
一个管道:源生成音频,变换重塑音频,接收端消费音频。如果没有接收端,在终端中打开的文件会打开播放器,并处于暂停状态:按空格键播放。通过管道传输、重定向或使用 --json 时,默认接收端为 stat,会打印概览。audio record 未指定接收端时,会保存为 recording.wav。
# sources
FILE path, URL, or glob ('*.wav' for batch)
- stdin (or omit when piping)
record capture from microphone
# transforms (chained left-to-right)
gain fade trim normalize crop
clip remove reverse repeat pad
speed stretch pitch insert mix
crossfade remix pan split resample
highpass lowpass eq lowshelf highshelf
notch bandpass allpass vocals dither
crossfeed shrink crossover match spectral
repair copy cut paste plugin
# sinks (terminate the chain — at most one)
stat [NAMES...] print analysis (default)
play [loop] open player UI
save PATH encode and write (or `-` for stdout); `192k` bitrate, `24bit` depth
# options
-f --force overwrite existing output
--format FMT override output format
--macro FILE apply edits from JSON
--cue FILE split at cue-sheet tracks (with split)
--verbose show progress
--help, -h help (or per-op: `audio gain --help`)
--mcp serve the CLI to AI agents as an MCP tool (stdio)
--plugins [NAME] the native plugins @audio/host finds, or one's parameters
# a native plugin (npm i @audio/host): by name, its parameters as name:value
audio voice.wav plugin "RX 12 De-click" sensitivity:6 save clean.wav
# named options, after an op or sink: name:value
normalize -27 lufs ceiling:-2 ducker key:voice.wav save out.m4a codec:alac
# compatibility shortcuts
-p ⇔ play -l ⇔ play loop -o PATH ⇔ save PATH
播放
␣ 暂停 · ←/→ 快进/快退 10 秒 · ⇧←/⇧→ 快进/快退 60 秒 · ↑/↓ 调节音量 · l 循环播放 · s 另存为 · q 退出
# play full song
audio song.mp3 play
# play fragment
audio song.mp3 10s..15s play
# play and loop a hook
audio song.mp3 30s..45s play loop
# play with effects applied live (streamable ops)
audio song.mp3 normalize broadcast highpass 80hz play
编辑
# clean up
audio raw-take.wav trim -30db normalize podcast fade 0.3s -0.5s save clean.wav
# scope a range (applies to whole chain)
audio in.wav 1s..10s gain -3db save out.wav
# range on a single op
audio in.wav gain -3db 1s..10s save out.wav
# filter chain
audio in.mp3 highpass 80hz lowshelf 200hz -3db save out.wav
# concat
audio intro.mp3 + content.wav + outro.mp3 trim normalize fade 0.5s -2s save ep.mp3
# crossfade into next
audio track1.mp3 crossfade track2.mp3 2s save mixed.wav
# voiceover
audio bg.mp3 gain -12db mix narration.wav 2s save mixed.wav
# music bed ducked under the voice (sidechain)
audio bed.mp3 ducker key:voice.wav mix voice.wav save episode.wav
# loudness to any target, true peak held; delivery settings
audio book.wav normalize -20 lufs save book.mp3 192k
# fix a video's sound, keep the picture
audio talk.mp4 highpass 80hz 4 normalize podcast save talk.clean.mp4
# master to a reference track (tone, width, loudness)
audio mix.wav master reference.wav save master.wav
# shorten pauses; the same cuts as an EDL for the video editor (.fcpxml, .otio too)
audio talk.mp4 shrink 0.3 save talk.edl
# split
audio audiobook.mp3 split 30m 60m save 'chapter-{i}.mp3'
audio album.wav split --cue album.cue save '{i} - {title}.mp3' # cue-sheet tracks, tagged
# record
audio record 30s save voice.wav
分析
# overview (the default sink when piped or redirected)
audio speech.wav stat
# range overview
audio speech.wav 0..10s stat
# specific stats
audio speech.wav stat loudness rms
# tempo / beat grid / onsets
audio track.mp3 stat bpm
audio track.mp3 stat beats onsets
# loudness to spec: integrated, max momentary / short-term, speech only, true peak
audio mix.wav stat loudness momentary shortterm dialog truepeak
# pass or fail against a delivery spec (exit 1 on a fail); --json for scripts
audio episode.wav normalize podcast check podcast
audio chapter.wav check acx --json
# pitch / chords / key
audio song.mp3 stat notes
audio song.mp3 stat chords
audio song.mp3 stat key
# spectrum / cepstrum with bin count
audio speech.wav stat spectrum 128
audio speech.wav stat cepstrum 13
# stat after transforms (transforms apply, then stat)
audio speech.wav gain -3db stat db
批处理
audio '*.wav' trim normalize podcast save '{name}.clean.{ext}'
audio '*.wav' gain -3db save '{name}.out.{ext}'
audio 'chapters/*.mp3' check acx # a whole audiobook: one line per chapter
在 CI 中检查
不符合规格的文件会导致构建失败:每个文件占一行,显示 ✓ 或违反的规则;只要有一项失败,退出码即为 1。
# .github/workflows/audio.yml
on: [push, pull_request]
jobs:
audio:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: audiojs/audio@master # or any CI: npx -y audio 'episodes/*.mp3' check podcast
with:
files: episodes/*.mp3
spec: podcast # spotify, broadcast, netflix, acx
Stdin/stdout
cat in.wav | audio gain -3db save - > out.wav
curl -s https://ex.com/speech.mp3 | audio normalize save clean.wav
Tab completion
eval "$(audio --completions zsh)" # add to ~/.zshrc
eval "$(audio --completions bash)" # add to ~/.bashrc
audio --completions fish | source # fish
常见问题
- 支持哪些格式?
- 解码:WAV、MP3、FLAC、OGG Vorbis、Opus、AAC、AIFF、CAF、WebM、AMR、WMA、QOA,通过 decode 实现。编码:WAV、MP3、FLAC、Opus、OGG、AIFF,通过 encode 实现。编解码器基于 WASM,并在首次使用时延迟加载。
- 需要 ffmpeg 或原生插件吗?
- 不需要 ffmpeg:编解码器和处理功能均由 JS 和 WASM 实现。在 Node 中播放和录制会使用一个小型原生插件,该插件已为 macOS、Linux(x64、arm64)和 Windows(x64)预构建,因此安装时无需编译;其他平台则会回退到
ffplay、SoX 或aplay。使用命令行时,请全局安装:npm i -g audio。 - 它有多大?
- 在网页中:整个库压缩并 minify 后为 128 KB(
dist/audio.min.js);编解码器和插件会在首次使用时通过import()加载,因此不会获取未使用的格式。在 Node 中:npm i audio会安装 327 个包,共 16 MB(包含所有编解码器和插件),无需编译;npm i audio --omit=optional则会安装 115 个包,共 11.5 MB(包含编解码器和核心功能:滤波器、均衡器、响度、音高、时长伸缩),之后可在使用时按名称安装各个插件系列(npm i @audio/dynamics)。 - 它如何处理大文件?
- 音频存储在固定大小的页面中。在浏览器中,使用
{ storage: 'persistent' }(或在存在 OPFS 时使用'auto')后,当内存超出预算时,冷页面会转移到 OPFS;预算根据navigator.storage.estimate()自动确定(配额的 1/4,64MB..512MB),也可通过{budget}覆盖;每个实例各自维护存储区,其副本共享该存储区。解码后的文件、audio.from(pcm)和推送流均适用。统计信息始终驻留在内存中(2 小时立体声约占 7 MB)。 - 编辑会破坏原始数据吗?
- 不会。
a.gain(-3).trim()会将条目加入编辑列表,不会触碰源页面。编辑会在read()/save()/for await时重新执行。 - 可以在浏览器中使用吗?
- 可以,API 相同。有关打包选项和导入映射,请参阅 浏览器。
- 必须等整个文件加载完成才能开始处理吗?
- 不用。播放、编辑和结构操作(裁剪、重复、填充、插入等)都会在解码期间逐步流式处理,输出会在文件加载完成前开始。编辑计划会随着数据到达而重新编译,并跟踪每个操作的安全输出边界。只有依赖总长度的操作(反转到文件开头、负数
at)才会等待完整解码。 - 支持 TypeScript 吗?
- 支持,随附
audio.d.ts。 - 功能是否与 FFmpeg / SoX / librosa 相当?
- 是——audiojs 生态系统涵盖 FFmpeg 滤波器、SoX 效果、librosa 分析、Pedalboard 和 MIREX 的实用基础功能,这些功能均作为
@audio/*插件提供,并由audio通过一个 API 统一调用(少数未覆盖的项目较为冷门或有意跳过)。每种效果、滤波器、生成器和分析器都位于注册表中,按名称调用即可在首次使用时加载。覆盖情况对照表见:docs/comparison.md。 - 它与 SoX / FFmpeg / Audacity / librosa / Web Audio / Tone.js 有何不同?
- 一句话概括:
audio是唯一一个能在 Node 和浏览器中使用同一套 API,并支持在解码期间流式处理的非破坏性延迟编辑的工具。原生工具(SoX、FFmpeg)在原始吞吐量方面更快,但没有 JS API、浏览器支持或撤销功能;浏览器库(Web Audio、Tone.js、Howler)是实时图形处理工具,而非文件编辑器。与 pydub、librosa、aubio、essentia、Pedalboard、SoX、FFmpeg、Audacity 和 MATLAB 的完整功能及性能对照表见 docs/comparison.md。
- decode – 编解码器解码(13+ 种格式)
- encode – 编解码器编码
- filter – 滤波器(加权、均衡、听觉)
- speaker – 音频输出
- mic – 音频输入
- pitch – 音高、和弦、调性分析
- audio-type – 格式检测
- pcm-convert – PCM 格式转换
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。