mcpField
T
tonejs

tonejs

github.com/plyght开发4290 次安装40 周前更新
代码仓库

关于 tonejs

tonejs 是一项指导使用 Tone.js 和 Web Audio API 构建浏览器交互式音乐应用的技能,面向音频应用开发者。内容涵盖合成、采样、节奏调度、效果处理及音画同步。

主要功能

  1. 01创建合成器与多音乐器
  2. 02播放音频文件与采样
  3. 03按音乐时间调度事件
  4. 04串接效果并自动化参数
  5. 05同步音频与视觉更新

适用场景

  1. 01构建步进音序器或鼓机
  2. 02为游戏添加音乐和音效
  3. 03制作实时音频可视化
  4. 04实现精确定时的音乐播放

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/plyght/tonejs-skill --skill tonejs

安装后在 Claude Code 中输入/tonejs确认已加载。

附带文件README.md

详细介绍

Tone.js 技能

通过 Tone.js 的高级抽象,使用 Web Audio API 在浏览器中构建交互式音乐应用。

何时使用此技能

在以下场景中使用 Tone.js:

  • 创建合成器、采样器或乐器
  • 构建步进音序器、鼓机或 DAW
  • 为游戏添加音效或音乐
  • 实现与声音同步的音频可视化
  • 使用效果器实时处理音频
  • 以精准时序安排音乐事件
  • 处理音乐概念(音符、速度、小节)

核心概念

1. 上下文与初始化

AudioContext 必须由用户交互触发启动(浏览器要求):

import * as Tone from "tone";

// ALWAYS call Tone.start() from user interaction
document.querySelector("button").addEventListener("click", async () => {
	await Tone.start();
	console.log("Audio context ready");
	// Now safe to play audio
});
2. 音频图与路由

所有音频节点都连接到通向 Tone.Destination(扬声器)的图中:

// Basic connection
const synth = new Tone.Synth().toDestination();

// Chain through effects
const synth = new Tone.Synth();
const filter = new Tone.Filter(400, "lowpass");
const delay = new Tone.FeedbackDelay(0.125, 0.5);
synth.chain(filter, delay, Tone.Destination);

// Parallel routing (split signal)
const reverb = new Tone.Reverb().toDestination();
const delay = new Tone.Delay(0.2).toDestination();
synth.connect(reverb);
synth.connect(delay);
3. 时间与调度

Tone.js 使用音乐记谱法抽象时间:

  • "4n" = 四分音符
  • "8n" = 八分音符
  • "2m" = 两个小节
  • "8t" = 八分音符三连音
  • 数字 = 秒

重要:始终使用传递给回调的 time 参数:

// CORRECT - sample-accurate timing
const loop = new Tone.Loop((time) => {
	synth.triggerAttackRelease("C4", "8n", time);
}, "4n");

// WRONG - JavaScript timing is imprecise
const loop = new Tone.Loop(() => {
	synth.triggerAttackRelease("C4", "8n"); // Will drift
}, "4n");
4. Transport 系统

用于同步事件的全局计时器:

// Schedule events on the Transport
const loop = new Tone.Loop((time) => {
	synth.triggerAttackRelease("C4", "8n", time);
}, "4n").start(0);

// Control the Transport
Tone.Transport.start();
Tone.Transport.stop();
Tone.Transport.pause();
Tone.Transport.bpm.value = 120; // Set tempo

分步说明

任务 1:创建基础合成器
  1. 导入 Tone.js
  2. 创建合成器并连接到输出
  3. 等待用户交互以启动音频
  4. 使用 triggerAttackRelease 播放音符
import * as Tone from "tone";

const synth = new Tone.Synth().toDestination();

button.addEventListener("click", async () => {
	await Tone.start();
	// Play C4 for an eighth note
	synth.triggerAttackRelease("C4", "8n");
});
任务 2:创建复音乐器
  1. 使用 PolySynth 封装单音合成器
  2. 传入多个音符来演奏和弦
  3. 按需释放指定音符
const polySynth = new Tone.PolySynth(Tone.Synth).toDestination();

// Play a chord
polySynth.triggerAttack(["C4", "E4", "G4"]);

// Release specific notes
polySynth.triggerRelease(["E4"], "+1");
任务 3:加载并播放音频文件
  1. 创建 Player 或 Sampler
  2. 等待 Tone.loaded() Promise 完成
  3. 开始播放
const player = new Tone.Player("https://example.com/audio.mp3").toDestination();

await Tone.loaded();
player.start();

// For multi-sample instruments
const sampler = new Tone.Sampler({
	urls: {
		C4: "C4.mp3",
		"D#4": "Ds4.mp3",
		"F#4": "Fs4.mp3",
	},
	baseUrl: "https://example.com/samples/",
}).toDestination();

await Tone.loaded();
sampler.triggerAttackRelease(["C4", "E4"], 1);
任务 4:创建循环模式
  1. 使用 Tone.Loop 或 Tone.Sequence 创建模式
  2. 将 time 参数传递给乐器触发函数
  3. 启动循环和 Transport
const synth = new Tone.Synth().toDestination();

const loop = new Tone.Loop((time) => {
	synth.triggerAttackRelease("C4", "8n", time);
}, "4n").start(0);

await Tone.start();
Tone.Transport.start();
任务 5:添加效果处理
  1. 创建效果器实例
  2. 按所需顺序连接(串联或并联)
  3. 按需调整湿/干混合比例
const synth = new Tone.Synth();
const distortion = new Tone.Distortion(0.4);
const reverb = new Tone.Reverb({
	decay: 2.5,
	wet: 0.5, // 50% effect, 50% dry
});

synth.chain(distortion, reverb, Tone.Destination);
任务 6:自动化参数
  1. 通过属性访问参数(例如 frequency、volume)
  2. 使用 rampTo、linearRampTo、exponentialRampTo 等方法
  3. 使用 time 参数安排更改
const osc = new Tone.Oscillator(440, "sine").toDestination();

osc.start();

// Ramp frequency to 880 Hz over 2 seconds
osc.frequency.rampTo(880, 2);

// Set value at specific time
osc.frequency.setValueAtTime(440, "+4");

// Exponential ramp (better for frequency)
osc.frequency.exponentialRampTo(220, 1, "+4");
任务 7:将视觉效果与音频同步
  1. 使用 Tone.Draw.schedule() 更新视觉效果
  2. 在与音频事件相同的回调中安排更新
  3. 视觉效果会在音频播放前立即更新
const loop = new Tone.Loop((time) => {
	synth.triggerAttackRelease("C4", "8n", time);

	// Schedule visual update
	Tone.Draw.schedule(() => {
		element.classList.add("active");
	}, time);
}, "4n");

常见模式

模式:步进音序器
const synth = new Tone.Synth().toDestination();
const notes = ["C4", "D4", "E4", "G4"];

const seq = new Tone.Sequence(
	(time, note) => {
		synth.triggerAttackRelease(note, "8n", time);
	},
	notes,
	"8n"
).start(0);

Tone.Transport.start();
模式:概率播放
const loop = new Tone.Loop((time) => {
	if (Math.random() > 0.5) {
		synth.triggerAttackRelease("C4", "8n", time);
	}
}, "8n");
模式:动态效果器参数
const filter = new Tone.Filter(1000, "lowpass").toDestination();
const lfo = new Tone.LFO(4, 200, 2000); // 4Hz, 200-2000Hz range

lfo.connect(filter.frequency);
lfo.start();

音效设计原则

核心见解

听觉处理速度比视觉快 10 倍(约 25 ms 对比约 250 ms)。声音能提供即时反馈,让交互感觉更灵敏。即使视觉反馈完全相同,有点击声的按钮也会让人感觉比没有点击声的按钮响应更快。

声音能瞬间传达情绪。 单个音调传达成功、错误或紧张感,比视觉编排更有效。音频和视觉共同讲述同一个故事时,体验会比单独使用任一形式更出色。

少即是多。 大多数交互应保持静音。将声音留给重要时刻:重要操作的确认、不可忽视的错误、状态转换和通知。为确保无障碍体验,声音始终应与视觉反馈配合——声音用于增强效果,而非取代视觉反馈。参考游戏的设计——它们在信息丰富、富有情感且不打扰人的音频反馈方面已经做得非常成熟。

设计理念

优秀的声音设计能够提升各种平台上的用户体验,包括 Web 应用、移动应用、桌面应用和游戏。这些原则普遍适用,无论是设计通知声音、UI 反馈还是音乐交互。

声音是一种人人都能理解的通用语言。设计音频时:

先思考基本问题:

  • 这个应用/功能的本质是什么?
  • 你想唤起什么情绪?
  • 它如何与应用的视觉美学相匹配?
  • 用户不看屏幕时,如何理解这次交互?

考虑使用情境:

  • 用户会在哪里听到声音?(口袋里、办公桌旁、繁忙的街道上、安静的房间里)
  • 用户当时在做什么?(工作、通勤、玩游戏)
  • 用户会多久听到一次?(每天一次还是数百次)
通知声音设计

有效的通知声音具有以下特点:

1. 易于区分

  • 创造独特的声音标识,让人能识别出应用
  • 使用具有特色的音色或旋律模式
  • 将简单元素叠加,增强辨识度
  • 不要模仿系统默认声音或其他常见声音

2. 传达含义

  • 声音应与消息相关联(不必直白,但要有所暗示)
  • 水声或天气相关内容可用流动感音色,提醒可用金属音色,成功提示可用温暖音色
  • 用音色和包络来暗示内容
  • 采用抽象表现,而非音效

3. 友好且恰当

  • 让紧迫程度与消息的重要性相匹配
  • 柔和的声音:起音缓和(50ms 以上),音色平滑(正弦波、三角波)
  • 紧急的声音:起音快速(<5ms),音色明亮(方波、FM 合成)
  • 音量和明亮度可体现优先级

4. 简单清晰

  • 避免复杂的叠加或密集的谐波内容
  • 一到两个音符的模式比旋律更合适
  • 反复聆听时,悦耳的音色更容易让人接受
  • 清晰胜过巧思

5. 不打扰且适合重复播放

  • 时长:通知声音最长为 0.3-0.8 秒
  • 高频播放的声音应采用较柔和的音色(正弦波、三角波)
  • 避免刺耳、复杂或过于明亮的音色
  • 每天听到 50 次以上时仍应悦耳

6. 能穿透噪声,但不刺耳

  • 中频(300-3000Hz)最有效
  • 避免极高频(>8kHz)和极低频(<80Hz)
  • 针对嘈杂环境进行设计,同时避免声音刺耳
  • 三角波和正弦波比方波更柔和
UI 声音设计

对于按钮、交互和转场:

1. 少量使用

  • 仅在重要的状态变化或确认操作时添加声音
  • 静音往往是最佳选择
  • 不要为每次悬停、鼠标经过或细微交互都添加声音
  • 仅在有意义的时刻提供音频反馈

2. 音量应符合用途

  • UI 声音:比通知声音低得多(-20 至 -30dB)
  • 用户手持设备,细微的声音更合适
  • 通知:音量应更高,以穿透环境噪声(-10 至 -15dB)

3. 同步至关重要

  • 音频和视觉更新必须同时发生(容差 < 10ms)
  • 不同步会让人感觉迟钝、不专业
  • 使用精确调度,而非大致计时

4. 与交互特征相匹配

  • 快速点击:时长短(<50ms),频率较高
  • 重按:时长较长(100-200ms),频率较低
  • 拖动操作:提供连续反馈或保持静音
  • 操作成功:音高上升或音色明亮
  • 操作失败:音高下降或音色暗沉

5. 传达深度和移动感

  • 向前移动:音高随时间上升
  • 向后移动:音高随时间下降
  • 打开:包络扩展(淡入)
  • 关闭:包络收缩(淡出)
  • 包络形状可暗示物理动作
创意流程

1. 从音色素材开始

  • 探索不同的合成类型(正弦波、三角波、方波、FM、AM、噪声)
  • 录制或合成与概念相关的声音
  • 实物、日常声音和传统乐器都适用
  • 构建包含 5-10 种候选声音的音效库

2. 让声音匹配用途

  • 每个 UI 元素都应有一种符合其特定用途的声音
  • 按钮点击:快速且灵敏(高音、快速衰减)
  • 切换开关:开和关状态使用两种不同的声音
  • 通知:更长、更有辨识度
  • 无需提供多个选项,选出最合适的即可

3. 使用任意声音来源

  • 传统乐器:卡林巴琴、铃铛、钢琴、马林巴琴
  • 实物材料:金属、玻璃、木材、塑料
  • 合成声音:纯音、FM 合成、滤波噪声
  • 非常规来源:烤面包机、门、机械声
  • 声音来源不如最终音色特征重要

4. 通过分层丰富音色

  • 组合 2-3 个简单元素,而不是使用一种复杂声音
  • 基础音调 + 泛音层 + 质感
  • 每一层都各有用途(饱满度、明亮度、特征)
  • 即使加入分层,也要保持总时长简短
技术注意事项

1. 音频清晰

  • 淡入/淡出以避免咔嗒声(10-50ms)
  • 移除直流偏移
  • 统一标准化音量
  • 避免截断或突然结束

2. 频率滤波

  • 使用 80Hz 高通滤波器(去除超低音)
  • 小型扬声器无法重现低频
  • 必要时使用 8kHz 低通滤波器(去除刺耳的高频)
  • 将能量集中在 300-3000Hz,以达到最佳效果

3. 跨平台设计

  • 以最差的扬声器系统为设计基准(手机、笔记本电脑)
  • 耳机能呈现更多细节,但应针对扬声器进行优化
  • 完全避免超低音(<80Hz)
  • 中频适用于各种设备

4. 时长指南

  • 通知:0.3-0.8 秒
  • 按钮点击:0.05-0.1 秒
  • 切换开关:0.05-0.15 秒
  • 过渡:0.1-0.3 秒
  • 所有 UI 声音都应简短,以尊重用户的注意力

5. 用户控制

  • 始终提供关闭声音的设置
  • 音量控制应独立于系统音量
  • 按声音类型分别设置开关(通知与 UI 反馈)
  • 遵循系统范围的静音开关设置

6. 精确同步

  • 将音频安排在与视觉内容完全相同的时刻播放
  • 10-20ms 的延迟可以被察觉,而且会让人感觉不对
  • 使用精确的定时 API,而非 setTimeout/setInterval
  • 音画同步对感知响应速度至关重要
设计检查清单

设计声音时的实现注意事项:

  • 易于区分:使用独特的波形/包络,避免模仿系统默认声音
  • 适合重复播放:使用简单、悦耳的音色(正弦波、三角波),而非复杂或刺耳的音色
  • 跨平台:频率介于 300-3000Hz,并使用 80Hz 高通滤波
  • 清晰可闻但不刺耳:使用中频,避免极高频(>8kHz)和极低频(<80Hz)
  • 时长简短:通知为 0.3-0.8 秒,UI 反馈短于 0.1 秒
  • 同步:音频触发应安排在视觉更新的同一时刻(容差 < 10ms)
  • 用户控制:提供关闭声音和调节音量的设置选项
  • 特征恰当:包络应与交互相匹配(快速轻触 = 短衰减,重按 = 较长衰减)
  • 音频清晰:通过淡入/淡出避免咔嗒声,并滤除不需要的频率
  • 表达含义:音色/音高应暗示信息内容(雨声对应液体感,提示音对应金属感,成功对应温暖感)

重要边缘情况和注意事项

1. 浏览器自动播放策略

必须在用户交互时调用 Tone.start()。否则不会播放任何音频。

// WRONG - will fail silently
Tone.Transport.start();

// CORRECT
button.addEventListener("click", async () => {
	await Tone.start();
	Tone.Transport.start();
});
2. 内存管理

完成后始终释放节点:

const synth = new Tone.Synth().toDestination();

// When finished
synth.dispose();

// For arrays of instruments
players.forEach((player) => player.dispose());
3. 定时精度

JavaScript 回调并不精确。始终使用时间参数:

// WRONG - will drift out of sync
setInterval(() => {
	synth.triggerAttackRelease("C4", "8n");
}, 250);

// CORRECT - sample-accurate
new Tone.Loop((time) => {
	synth.triggerAttackRelease("C4", "8n", time);
}, "4n").start(0);
4. 加载采样

播放前等待采样加载完成:

const sampler = new Tone.Sampler({
	urls: { C4: "piano.mp3" },
	baseUrl: "/audio/",
}).toDestination();

// WRONG - may not be loaded yet
sampler.triggerAttack("C4");

// CORRECT
await Tone.loaded();
sampler.triggerAttack("C4");
5. 单音与复音

基础合成器是单音的(一次播放一个音符):

// Only plays one note
const mono = new Tone.Synth().toDestination();
mono.triggerAttack(["C4", "E4", "G4"]); // Only C4 plays

// Plays all notes
const poly = new Tone.PolySynth(Tone.Synth).toDestination();
poly.triggerAttack(["C4", "E4", "G4"]); // All play
6. 音符格式

音符可以通过多种方式指定:

synth.triggerAttackRelease("C4", "8n"); // Pitch-octave notation
synth.triggerAttackRelease(440, "8n"); // Frequency in Hz
synth.triggerAttackRelease("A4", "8n"); // A4 = 440Hz
7. 传输时间与音频上下文时间

存在两种计时系统:

Tone.now(); // AudioContext time (always running)
Tone.Transport.seconds; // Transport time (starts at 0)

// Schedule on AudioContext
synth.triggerAttackRelease("C4", "8n", Tone.now() + 1);

// Schedule on Transport
Tone.Transport.schedule((time) => {
	synth.triggerAttackRelease("C4", "8n", time);
}, "1m");

架构概览

ToneAudioNode (base class)
├── Source (audio generators)
│   ├── Oscillator, Player, Noise
│   └── Instrument
│       ├── Synth, FMSynth, AMSynth
│       ├── Sampler
│       └── PolySynth
├── Effect (audio processors)
│   ├── Filter, Delay, Reverb
│   ├── Distortion, Chorus, Phaser
│   └── PitchShift, FrequencyShifter
├── Component (building blocks)
│   ├── Envelope, Filter, LFO
│   └── Channel, Volume, Panner
└── Signal (parameter automation)
    ├── Signal, Add, Multiply
    └── Scale, WaveShaper

快速参考

乐器类型
  • Tone.Synth - 基础单振荡器合成器
  • Tone.FMSynth - 频率调制合成
  • Tone.AMSynth - 振幅调制合成
  • Tone.MonoSynth - 带滤波器和包络的单声部合成器
  • Tone.DuoSynth - 双声部合成器
  • Tone.MembraneSynth - 打击乐合成器
  • Tone.MetalSynth - 金属音色
  • Tone.NoiseSynth - 基于噪声的合成
  • Tone.PluckSynth - 拨弦模型
  • Tone.PolySynth - 多声部封装器
  • Tone.Sampler - 多采样乐器
常见效果器
  • Tone.Filter - 低通、高通、带通等滤波器
  • Tone.Reverb - 卷积混响
  • Tone.Delay / Tone.FeedbackDelay - 回声效果
  • Tone.Distortion - 波形塑形失真
  • Tone.Chorus - 合唱效果
  • Tone.Phaser - 移相效果
  • Tone.PitchShift - 实时移调
  • Tone.Compressor - 动态范围压缩
  • Tone.Limiter - 砖墙限制器
时间记法
  • "4n" - 四分音符
  • "8n" - 八分音符
  • "16n" - 十六分音符
  • "2m" - 两小节
  • "8t" - 八分音符三连音
  • "1:0:0" - 小节:拍:十六分音符
  • 0.5 - 秒(数字)

资源

评论

支持 Markdown