资讯动态

Tone.js 实战指南:用 Web Audio API 在浏览器中构建交互式音乐应用

发布时间:2026/10/15 21:02:12 来源:尧图企业网站定制
【免费下载链接】chrome-music-labA collection of experiments for exploring how music works, all built with the Web Audio API.项目地址https://gitcode.com/gh_mirrors/ch/chrome-music-lab点击查看免费下载Tone.js 是一个面向浏览器的 Web Audio 合成框架本指南以 chrome-music-lab 仓库中捆绑的 Tone.js 0.6.1 源码soundspinner/third_party/Tone.js为事实依据系统讲解其架构理念、安装方式、时间系统、Transport 调度、乐器与效果器、信号机制等核心能力并穿插 Sound Spinner 等实验项目的真实调用证据。读完本文你将掌握用 Tone.js 编写可运行的合成器、节奏循环与效果器链的完整实战方案。一、Tone.js 是什么面向音乐人与音频程序员的 Web Audio 框架Tone.js 的设计目标是在浏览器中创建交互式音乐。它的架构同时照顾两类使用者音乐人与 DAW 用户高层 API 直接提供数字音频工作站级别的能力包括全局 Transport负责调度与计时事件、预制合成器synths与效果器effects开箱即用信号处理程序员熟悉 Max/MSP 等语言底层提供大量高性能、低延迟的构建块与 DSP 模块可用于自建合成器、效果器和复杂的控制信号。在chrome-music-lab仓库中Tone.js 以源码目录形式随各实验捆绑如 soundspinner/third_party/Tone.js/Tone版本为 0.6.1见 soundspinner/third_party/Tone.js/package.json。整个chrome-music-lab是探索音乐如何运作的实验集合Tone.js 正是其中大部分音频实验的底层引擎。二、安装与引入方式原文档提供了三种安装途径均可实际使用# 方式一bower bower install tone # 方式二npm npm install tone此外官方还提供 CDN 引入方式完整版与压缩版 Tone.min.js。在chrome-music-lab这类仓库中更常见的做法是像 soundspinner/third_party/Tone.js、arpeggios/third_party/Tone.js 那样将源码直接捆绑进项目通过 RequireJS 的 AMD 模块机制按需加载例如 Sound Spinner 的入口文件require([Tone/core/Tone, Tone/source/Microphone], function(Tone, Microphone){ // 使用 Tone.context、Tone.Microphone 等 });参见 soundspinner/app/Main.js。仓库中 Tone.js 源码采用 AMDdefine包装每个类一个文件可按需require这正是它适合实验型项目捆绑使用的原因。三、Hello Tone第一个合成器原文档给出了最简示例这段代码可以直接放进任意支持 Web Audio 的浏览器运行//创建 Tone 内置合成器并连接到主输出 var synth new Tone.SimpleSynth().toMaster(); //以八分音符的时长演奏中央 C synth.triggerAttackRelease(C4, 8n);从源码看Tone.SimpleSynth 由一个Tone.OmniOscillator直接串联一个Tone.AmplitudeEnvelope组成this.oscillator.chain(this.envelope, this.output)默认配置为三角波振荡器 标准 ADSR 包络attack 0.005s、decay 0.1s、sustain 0.3、release 1s。toMaster()是connect(Tone.Master)的简写Tone.Master是初始化时自动创建的单例主输出节点见 Tone/core/Master.js。triggerAttackRelease的完整参数语义如下参数类型说明第一个参数Frequency频率可传数字如440也可传音高-八度记法如D#2第二个参数Time包络 sustain 的持续时间即音符保持时长第三个参数可选Timeattack 开始的时间缺省时按 now 立即播放传入时间值则可预约未来播放triggerAttack与triggerRelease也可以分开调用两者组合即等价于triggerAttackRelease。从 Tone.Instrument 基类源码可见triggerAttackRelease本质是先在指定时间触发 attack再在time duration触发 release的封装。四、Time 时间系统数字、记谱法与表达式任何接受时间参数的方法都兼容数字与字符串两种形式数字按字面意思解释为秒字符串可编码相对当前速度BPM的时间表达式。例如4n是四分音符8t是八分音符三连音1m是一小节。任何以前缀的值都会叠加到当前时间上。预约一个小节之后触发同一音符synth.triggerAttackRelease(C4, 8n, 1m);从源码看时间解析全部收敛到Tone.prototype.toSecondsTone/core/Type.js它依次处理Now-Relative前缀去掉并把当前时间叠加到结果上isNowRelative用正则/^\s*\(.)/i判定括号数学表达式递归求值括号内内容再对 - * /拆分的组件逐一求值后用eval计算源码注释特别注明 eval is evil量化语法4n16n形式的量化需要依赖Tone.Transport.quantize未初始化 Transport 会抛错单值类型判定依次尝试记谱法isNotation如4n/2m/8t、TransportTimeisTransportTime如1:2:0、频率isFrequency如8hz、tickisTicks如48i最后 fallback 到parseFloat。记谱法的换算公式位于notationToSecondsTone/core/Type.js后缀nbeats 4 / subdivision一拍 60/BPM 秒后缀t三连音beats (4 / subdivision) * 2/3后缀m小节beats subdivision * timeSignature。TransportTime 格式为小节:四分音符:十六分音符如4:3:2表示 4 小节 3 个四分音符 2 个十六分音符由transportTimeToSeconds计算beats measures * timeSignature quarters sixteenths / 4再乘以60/bpm。五、Transport全局节拍器与事件调度时间表达式都基于 Transport 的 BPM 求值。Tone.Transport 是全局主节拍器为所有音源、信号与事件提供共享时间线上的全局同步。用Tone.Transport.schedule调度的回调会在预定时间点之前被调用且事件的精确时间会作为回调的第一个参数传入//在第一小节的第二个正拍beat上调度回调 Tone.Transport.schedule(function(time){ //用传入的精确时间触发合成器的 attackRelease synth.triggerAttackRelease(C4, 8n, time); }, 1:2:0); //启动 Transport Tone.Transport.start();源码实现Tone/core/Transport.js揭示了它的内部结构内部以Tone.Clock驱动默认PPQ每四分音符 tick 数 48初始 BPM 120默认时间签名分子为 4默认 swing 为 0、swingSubdivision 为16nbpm是一个可信号化的参数Tone.Transport.bpm.value 80直接改速度Tone.Transport.bpm.rampTo(120, 10)可在 10 秒内渐变到 120 BPM事件调度分三类存储_timeline按时间点触发的调度事件、_repeatedEvents重复事件、_onceEvents一次性事件每个 tick 由_processTick依次遍历触发并支持 swing 偏移与 loop 回绕回绕时会触发loop事件Transport 还作为 Emitter 发出start、stop、pause、loop事件回调同样携带事件时间。六、Loops高层循环事件类除了直接在 Transport 上调度事件Tone.js 还提供更高级的事件类。Tone.Loop 用于创建可按时间线启停的循环回调//每个四分音符演奏一个音符 var loop new Tone.Loop(function(time){ synth.triggerAttackRelease(C2, 8n, time); }, 4n); //让循环在 Transport 时间线的第 1 到第 4 小节之间运行 loop.start(1m).stop(4m);启动 Transport 即可听到循环音符Tone.Transport.start();从 Tone/event/Loop.js 源码看Tone.Loop内部包装了一个Tone.Event默认参数包括interval: 4n、playbackRate: 1、iterations: Infinity、probability: true、mute: false。它还暴露了若干实用的可读写属性interval相邻两次回调的时间间隔loop.interval 8n可动态改playbackRate播放速率2表示快一倍iterations循环次数默认Infinity无限循环humanize给调度时间加入 ±0.01s 的随机抖动让节奏更人性化probability每次回调实际被触发的概率mute静音循环不触发回调state/progress只读状态与进度0-1。七、乐器MonoSynth 与 PolySynthTone 的乐器都继承自同一个 Instrument 基类因此共享统一的触发 APItriggerAttack/triggerRelease/triggerAttackRelease并自带以分贝为单位的volume参数。Tone.MonoSynth 由一个振荡器、一个滤波器、两个包络组成振幅包络控制音量滤波包络控制滤波器截止频率。构造时可传入初始参数//为滤波器和滤波包络传入一些初始值 var monoSynth new Tone.MonoSynth({ filter : { type : lowpass, Q : 7 }, filterEnvelope : { attack : 0.02, decay : 0.1, sustain : 0.2, release : 0.9, } }).toMaster(); //在一秒后开始演奏 D3 monoSynth.triggerAttack(D3, 1);源码中MonoSynth.defaultsTone/instrument/MonoSynth.js给出了未覆盖参数的默认值可据此理解信号链oscillator → filter → envelope → output配置块默认值oscillatortype: square频率C4detune 0filtertype: lowpassQ: 6rolloff: -24envelopeattack 0.005 / decay 0.1 / sustain 0.9 / release 1filterEnvelopeattack 0.06 / decay 0.2 / sustain 0.5 / release 2baseFrequency 200、octaves 7、exponent 2所有乐器默认是单音monophonic单声部的但把乐器构造函数作为第二个参数传给 Tone.PolySynth 即可获得复音能力//一个 4 声部的 MonoSynth var polySynth new Tone.PolySynth(4, Tone.MonoSynth).toMaster(); //演奏一个和弦 polySynth.triggerAttackRelease([C4, E4, G4, B4], 2n);从 Tone/instrument/PolySynth.js 源码可见PolySynth本身不发声它只是按polyphony数量创建一组指定乐器的 voice 并管理分配triggerAttack时选取最久未被触发的 voicerelease值最小的槽位来演奏新音符triggerRelease则按音符匹配对应 voice。默认polyphony: 4、voice: Tone.MonoSynth且triggerAttackRelease接受单个音符或音符数组。八、效果器把合成器路由进效果链前面的示例中合成器都直接连到主输出但也可以先经过一个或多个效果器再进扬声器//创建一个失真效果器 var distortion new Tone.Distortion(0.4).toMaster(); //把合成器连到失真效果器 synth.connect(distortion);Tone.Distortion 的底层实现非常Tone 风格它内部使用一个 4096 点采样的Tone.WaveShaper按经典算法(3 k) * x * 20 * deg / (Math.PI k * |x|)k 失真量 × 100生成整形曲线distortion默认 0.4标称范围 0-1另有oversample参数可选none、2x、4x见 Tone/effect/Distortion.js。仓库中 Tone/effect 目录还包含 20 余种效果器Filter、Chorus、Phaser、PingPongDelay、Freeverb、Tremolo、Vibrato、BitCrusher、PitchShift 等均可按同样的connect方式串入信号链。九、音源振荡器、采样播放器、噪声与麦克风Tone 提供多类基础音源Tone.Oscillator支持 sine、square、triangle、sawtooth 波形还支持相位旋转、谐波 partials 与 Transport 同步Tone.Player采样播放器buffer playerTone.Noise噪声发生器两种特殊振荡器Tone.PWMOscillator脉宽调制、Tone.PulseOscillator脉冲波Tone.Microphone外部音频输入需要浏览器支持 WebRTCMicrophone.supported可检测。原文档示例——创建一个立即连到扬声器并启动的 PWM 振荡器//一个连到扬声器并立刻启动的 pwm 振荡器 var pwm new Tone.PWMOscillator(Bb3).toMaster().start();Sound Spinner 实验正是利用音源能力的真实案例它通过 Tone/source/Microphone 采集麦克风输入用Tone.context.createScriptProcessor与Tone.context.createBuffer做录音缓冲见 soundspinner/app/mic/Recorder.js 与 soundspinner/app/mic/Player.js并配合StartAudioContext(Tone.context, ...)在用户交互后解锁音频上下文见 soundspinner/app/Main.js。十、Signals音频速率的信号控制与底层 Web Audio API 一致Tone.js 几乎所有东西都由音频速率audio-rate信号控制。这带来一个强大特性可以用单一信号对多个参数做样本级精确的同步控制。关键实现细节Tone/signal/Signal.js信号完全不用ScriptProcessorNode实现因此几乎不引入额外处理开销、也没有额外延迟信号运算与逻辑全部委托给原生的GainNode与WaveShaperNode处理工作落在 API 底层的原生代码中完成实现方式是构造一个填充常量 1 的循环AudioBufferSourceNodeTone.Signal._constant作为基准通过 GainNode 增益缩放输出任意信号值一个Tone.Signal本质上就是一个包装了GainNode.gainAudioParam 的可调度值具备原生 AudioParam 的全部方法setValueAtTime、linearRampToValueAtTime等并额外支持rampTo等便捷方法信号连到其他信号或 AudioParam 时会接管其输出值。Tone 内部大量使用信号Transport 的bpm、振荡器的frequency/detune、乐器的volume都是信号同时它们也是做通用 DSP 与控制信号逻辑的好工具。Tone/signal 目录还包含 30 余个信号运算模块Add、Multiply、Scale、ScaleExp、Abs、Clip、Max、Min、Modulo、Select、Switch、Expr 表达式等可以像模块化合成器一样组合出复杂的控制信号。十一、AudioContext访问与替换Tone.js 在加载时就会创建一个 AudioContext 并做浏览器兼容垫片shim。从 Tone/core/Tone.js 源码可见它会依次补齐AudioContext/webkitAudioContext、createGain/createGainNode、createDelay/createDelayNode、OscillatorNode.start/stop等旧式 API 差异。默认上下文可通过Tone.context访问同时挂在原型上Tone.prototype.context如需与其他库共用上下文可用Tone.setContext(audioContext)替换——注意应在创建任何对象之前调用替换后所有已注册的_initAudioContext回调会以新上下文重新执行。另外 Tone 还预置了与上下文相关的常量Tone.now()返回当前上下文时钟时间blockTime128 样本/帧的时长与sampleTime单样本时长供底层计算使用。十二、MIDI 与性能设计MIDI要使用 MIDI 文件需要先用 MidiConvert 工具把 MIDI 转换成 Tone.js 能理解的 JSON 格式。chrome-music-lab中 pianoroll/midi 目录下的MinuetInG.json、beethoven5.json、preludeInC.json等文件正是这种被钢琴卷帘实验消费的 JSON 乐谱数据。性能Tone.js 全库只使用一个ScriptProcessorNode位于Tone.Meter中其余模块全部寻找原生 Web Audio 组件的替代方案尤其大量使用 GainNode 与 WaveShaperNode使 Tone.js 在桌面与移动浏览器上都能良好运行。之所以尽量避免 ScriptProcessorNode是因为它虽然功能强大但相比其他节点会引入较多延迟并可能产生爆音glitches。十三、在 chrome-music-lab 中的落地实践chrome-music-lab是一个探索音乐如何运作的实验合集Tone.js 以相同版本源码捆绑在 arpeggios、chords、melodymaker、pianoroll、soundspinner 五个实验中。它们各自展示了 Tone.js 不同维度的能力Sound Spinner基于Tone.MicrophoneTone.context实现录一段声音、像唱片一样旋转播放的互动玩法Melody Maker / Pianoroll把音符网格/钢琴卷帘映射到合成器触发体现triggerAttackRelease在交互界面的高频使用Arpeggios / Chords围绕音阶、和弦与分解和弦的实时发声依赖 Transport 与音高系统的精确换算。想深入验证本文所述的类与方法可直接阅读仓库内源码时间系统看 Tone/core/Type.jsTransport 看 Tone/core/Transport.js乐器看 Tone/instrument效果器看 Tone/effect信号看 Tone/signal音源看 Tone/source。每个实验的index.html与app/Main.js则提供了可直接运行的完整接线示例。赞分享【免费下载链接】chrome-music-labA collection of experiments for exploring how music works, all built with the Web Audio API.项目地址https://gitcode.com/gh_mirrors/ch/chrome-music-lab点击查看免费下载相关推荐chrome-music-lab 中的 Tone.js浏览器交互式音乐合成的 Web Audio 框架实战指南chrome music lab 中的 Tone.js浏览器交互式音乐合成的 Web Audio 框架实战指南 Tone.js 是 chrome musicTone.js完全指南Web Audio框架如何重塑浏览器音乐交互体验Tone.js完全指南Web Audio框架如何重塑浏览器音乐交互体验 你是否曾想过在网页中创建互动音乐体验却被复杂的Web Audio API挡在门外T音频处理前端chrome-music-lab 技术解析用 Web Audio、Tone.js 与 getUserMedia 构建浏览器音乐实验chrome music lab 技术解析用 Web Audio、Tone.js 与 getUserMedia 构建浏览器音乐实验 chrome music上一篇cangjie_deveco_plugins 静态检查规则 G.SER.03 详解保证序列化与反序列化的变量类型一致下一篇老游戏联机救星IPXWrapper 让 IPX 协议在 Win10/Win11 上原地复活3 分钟重开局域网对战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑