尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

three.js Audio 类深度解析:Web Audio API 驱动的全局音频对象

three.js Audio 类深度解析:Web Audio API 驱动的全局音频对象 three.js Audio 类深度解析Web Audio API 驱动的全局音频对象【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.jsAudio是 three.js 中表示非定位全局音频的核心类。它继承自Object3D基于 Web Audio API 与 src/audio/Audio.js 源码完整讲解Audio的构造、属性、方法与底层节点图帮助你在 three.js 项目中正确接入、控制并调试全局音频。一、类定位与继承体系Audio的继承链为EventDispatcher → Object3D → Audio见 docs/pages/Audio.html.md。从源码结构看// src/audio/Audio.js class Audio extends Object3D { constructor( listener ) { super(); this.type Audio; this.listener listener; // 全局音频监听器 this.context listener.context; // 复用以监听器共享的 AudioContext this.gain this.context.createGain(); this.gain.connect( listener.getInput() ); // 输出接入监听器输入节点 } }关键设计Audio本身不创建独立的AudioContext而是复用AudioListener持有的全局上下文。整个引擎的上下文由 src/audio/AudioContext.js 中的单例管理AudioContext.getContext()惰性创建window.AudioContext回退到webkitAudioContext也可通过setContext()从外部注入。每个Audio实例构造时都会创建一个专属GainNode即.gain并连接到listener.getInput()这条链路决定了音量控制与后续空间定位处理的走向。官方文档与源码注释均强调Audio是非定位音频与PositionalAudio定位音频相对。需要基于距离衰减、Panner 效果的场景应改用PositionalAudio背景音乐、按钮音等全局音效则直接使用Audio。二、快速上手构造与播放完整流程官方文档给出的标准用法继承自 src/audio/Audio.js 头注释// 1. 创建 AudioListener 并挂到相机上监听器随相机移动 const listener new THREE.AudioListener(); camera.add( listener ); // 2. 创建全局音频源 const sound new THREE.Audio( listener ); // 3. 加载音频文件并设为 Audio 的 buffer const audioLoader new THREE.AudioLoader(); audioLoader.load( sounds/ambient.ogg, function( buffer ) { sound.setBuffer( buffer ); sound.setLoop( true ); sound.setVolume( 0.5 ); sound.play(); });流程要点必须存在监听器new Audio( listener )的listener参数是全局唯一的AudioListener见 src/audio/AudioListener.js。其gain节点直接连接到context.destination声卡输出常规做法是把监听器作为相机子对象使声音与视角绑定。加载与解码分离AudioLoader负责把音频文件解码为AudioBuffer。内部实现src/loaders/AudioLoader.js通过FileLoader以arraybuffer方式拉取文件先buffer.slice( 0 )拷贝一份再解码——因为decodeAudioData会detach原 buffer直接解码会使原始数据无法复用解码通过AudioContext.getContext()全局单例完成并用manager.itemStart / itemEnd注册url #decode占位项避免 LoadingManager 在解码完成前就提前结束源码中标注了 issue #33378。setBuffer只是登记play才真正建源setBuffer()仅保存buffer并把sourceType置为buffer若autoplay为true则立即play()真正创建AudioBufferSourceNode发生在play()内部。仓库内可直接对照的示例examples/webaudio_timing.html——演示play( delay )定时播放与多个音效的时序编排examples/webaudio_sandbox.html——麦克风setMediaStreamSource与振荡器setNodeSource的沙盒音频素材目录 examples/sounds/包含ping_pong.mp3、button-press.ogg等文件。三、构造函数new Audio( listener )listener全局音频监听器AudioListener实例。构造后type为Audio。构造副作用源码 src/audio/Audio.js创建GainNode并连入listener.getInput()初始化全部状态字段autoplay false、buffer null、detune 0、loop false、loopStart / loopEnd / offset 0、duration undefined、playbackRate 1、isPlaying false、hasPlaybackControl true、source null、sourceType empty、filters []私有字段_startedAt 0、_progress 0、_connected false用于暂停/续播的进度追踪与连线状态。四、属性全表Properties以下属性说明完整继承自官方文档并结合源码给出默认值与读写方式属性类型默认值说明.autoplaybooleanfalse是否自动开始播放setBuffer后若为true立即play().bufferAudioBufferreadonlynull音频缓冲引用经setBuffer()设定.contextAudioContextreadonly—音频上下文来自监听器.detunenumberreadonly0音高偏移单位为音分cent±100 为半音±1200 为八度经setDetune()设定.durationundefined \| numberundefined覆盖音频默认时长播放的最长秒数.filtersArrayAudioNodereadonly[]低阶滤波器链如BiquadFilterNode经setFilters()设定.gainGainNodereadonly—音量控制节点即getOutput()返回值.hasPlaybackControlbooleanreadonlytrue是否可用play()/pause()等控制播放由各set*()方法自动设定.isPlayingbooleanreadonlyfalse是否正在播放由play()/pause()/stop()自动维护.listenerAudioListenerreadonly—全局音频监听器.loopbooleanreadonlyfalse是否循环播放经setLoop()设定.loopEndnumber0循环结束位置秒.loopStartnumber0循环开始位置秒.offsetnumber0播放起点的 buffer 内偏移秒.playbackRatenumberreadonly1播放速度经setPlaybackRate()设定.sourceAudioNodereadonlynull当前音频源引用由某个set*()方法自动设定.sourceTypeempty \| audioNode \| mediaNode \| mediaStreamNode \| bufferreadonlyempty源类型由set*()方法自动设定其中sourceType与hasPlaybackControl的对应关系是理解本类的关键只有setBuffer()会把hasPlaybackControl置为truebuffer类型其余三种set*()源都会置为false——这意味着只有 buffer 源支持play/pause/stop/setLoop/setPlaybackRate等控制方法其余调用会触发warn( Audio: this Audio has no playback control. )并直接返回源码 src/audio/Audio.js。五、播放控制方法play / pause / stop 的源码级行为.play( delay 0 ) : Audio | undefined启动播放仅对允许播放控制的源有效。源码实现src/audio/Audio.js揭示了几个文档未明说但很重要的细节play( delay 0 ) { if ( this.isPlaying true ) { warn( Audio: Audio is already playing. ); // 重复 play 仅警告 return; } if ( this.hasPlaybackControl false ) { warn( Audio: this Audio has no playback control. ); return; } this._startedAt this.context.currentTime delay; // 记录调度时刻 const source this.context.createBufferSource(); source.buffer this.buffer; source.loop this.loop; source.loopStart this.loopStart; source.loopEnd this.loopEnd; source.onended this.onEnded.bind( this ); source.start( this._startedAt, this._progress this.offset, this.duration ); // ^^^^ 定时 ^^^^ 续播位置 ^^^^ 时长上限 this.isPlaying true; this.source source; this.setDetune( this.detune ); this.setPlaybackRate( this.playbackRate ); return this.connect(); }每次play()都会新建一个AudioBufferSourceNodeWeb Audio 的 source 节点是一次性资源start后不能复用delay不是等 N 秒后再执行而是把source.start()的调度时间设为context.currentTime delay适合做帧精确的音效对齐examples/webaudio_timing.html 正是演示此用法this._progress this.offset支持pause后从断点续播duration作为第三个参数传入start()即最长播放秒数。.pause() : Audio | undefined暂停并记录进度src/audio/Audio.jsthis._progress Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate; if ( this.loop true ) { // 循环音频需保证 _progress 不超出时长 this._progress this._progress % ( this.duration || this.buffer.duration ); } this.source.stop(); this.source.onended null; this.isPlaying false;注意两点进度计算乘了playbackRate变速播放时进度与墙钟时间不同步pause会主动null掉onended回调避免误触发onEnded()把isPlaying复位并清零_progress。.stop( delay 0 ) : Audio | undefined停止播放并将_progress归零区别于pause保留进度若source存在则调用source.stop( context.currentTime delay )即也支持延迟停止。.onEnded()播放自然结束时由source.onended自动触发置isPlaying false并将_progress归零。可以理解为非 loop 音频的播放完毕钩子。六、源设置方法四种 sourceType各set*()方法在 src/audio/Audio.js 中实现行为一致设置sourceType与hasPlaybackControl保存source调用connect()建立连线返回this支持链式调用。方法sourceTypehasPlaybackControl源创建方式.setNodeSource( audioNode )audioNodefalse直接使用传入节点如OscillatorNode适合程序化合成.setMediaElementSource( mediaElement )mediaNodefalsecontext.createMediaElementSource( mediaElement )把audio/video元素引到 Web Audio 图.setMediaStreamSource( mediaStream )mediaStreamNodefalsecontext.createMediaStreamSource( mediaStream )如麦克风流.setBuffer( audioBuffer )buffertrue仅记录 buffer播放时由play()建BufferSource典型场景与官方示例对应// 振荡器源程序化声音对应 webaudio_sandbox 的思路 const osc listener.context.createOscillator(); osc.frequency.value 440; const tone new THREE.Audio( listener ); tone.setNodeSource( osc ); osc.start(); // 麦克风源 const stream await navigator.mediaDevices.getUserMedia( { audio: true } ); mic.setMediaStreamSource( stream );七、音量、音高与速度为什么都用 setTargetAtTime三个调节方法在源码中统一使用setTargetAtTime( value, context.currentTime, 0.01 )做 10ms 时间常数的指数平滑src/audio/Audio.js.setVolume( value )/.getVolume()直接作用于.gain.gain无需播放状态判断.setDetune( value )/.getDetune()仅在isPlaying true且source.detune ! undefined时写入当前 sourcedetune单位是音分cents±100 为半音、±1200 为八度.setPlaybackRate( value )/.getPlaybackRate()同样仅对播放中的 buffer 源生效无播放控制权限时警告并返回undefined。这种平滑方式避免了音量/音高突变产生的爆音click noise属于 Web Audio 工程中的惯用做法。getVolume()直接读取gain.gain.value而非缓存的 JS 变量。八、循环与播放偏移.setLoop( value )/.getLoop()仅对可播放控制的源生效若正在播放则同步写source.loop。.setLoopStart( value )/.setLoopEnd( value )以秒为单位划定 buffer 内循环区间0表示未显式设置区间这两个值在play()建源时一次性写入新 source。.offset直接赋值即可。play()以_progress offset作为起点因此可用来从 buffer 第 N 秒开始播。九、滤波器链connect / disconnect / setFiltersAudio内置了一个可插入的滤波器链用于在 source 与输出GainNode之间串接任意AudioNode常见为BiquadFilterNodeconst filter listener.context.createBiquadFilter(); filter.type lowpass; filter.frequency.value 500; sound.setFilter( filter ); // 等价于 setFilters( [ filter ] ).setFilters( value )/.setFilter( filter )/.getFilters()/.getFilter()setFilter是setFilters的单元素封装src/audio/Audio.js.connect()若filters.length 0按source → filters[0] → ... → filters[n] → gain顺序串联否则source → gain直连并置_connected true.disconnect()以_connected标志防重复断连镜像地断开同样的链路当音频正在连线时调用setFilters源码会先disconnect()、替换数组、再connect()保证换滤波器不产生残留连线src/audio/Audio.js。getFilter()返回filters[0]可能为undefinedgetFilters()返回整条链。十、.getOutput()与外部扩展.getOutput()返回该Audio的输出GainNodesrc/audio/Audio.js。这是把全局音频接入更复杂处理图的官方口子例如把getOutput()再连到一个ConvolverNode混响或AnalyserNode频谱可视化对应AudioAnalyser类而不破坏内部滤波器链。十一、copy / clone 的限制源码还包含文档未列出的两个行为src/audio/Audio.jscopy( source, recursive ) { super.copy( source, recursive ); if ( source.sourceType ! buffer ) { warn( Audio: Audio source type cannot be copied. ); return this; // 仅 buffer 源可复制 } // 复制 autoplay/buffer/detune/loop/loopStart/loopEnd/offset/ // duration/playbackRate/hasPlaybackControl/sourceType/filters } clone( recursive ) { return new this.constructor( this.listener ).copy( this, recursive ); }只有sourceType buffer的实例可被copy其余源节点、媒体元素、媒体流无法复制引用型源与具体 DOM/流绑定clone()复用同一个 listener新实例不会创建新的监听器。十二、监听器侧AudioListener 的关键机制Audio依赖的AudioListenersrc/audio/AudioListener.js值得了解其两个核心机制主音量setMasterVolume( value )作用于监听器自身gain连接context.destination影响场景内所有音频节点——Audio的setVolume是单源音量二者叠加生效每帧矩阵同步updateMatrixWorld()中把自身世界矩阵分解为位置/四元数/缩放计算 forward/up 向量后写入context.listener。源码针对 Chrome 的旧实现走了positionX / forwardX等 AudioParam 的linearRampToValueAtTime路径注释标注 issue #14393否则直接setPosition/setOrientation。虽然Audio是非定位音频但这条同步链是PositionalAudio空间化的基础且决定了监听器必须挂在场景图通常是相机中才能正确工作。十三、验证与延伸阅读单元测试test/unit/src/audio/Audio.tests.js 通过 mock listener 验证了Audio extends Object3D、可实例化且type Audio音频加载的测试见 test/unit/src/loaders/AudioLoader.tests.js同族类docs/pages/AudioListener.html.md、docs/pages/AudioLoader.html.md、docs/pages/PositionalAudio.html.md、docs/pages/AudioAnalyser.html.md源码目录 src/audio/可运行示例examples/webaudio_timing.html定时播放、examples/webaudio_sandbox.html麦克风/合成、examples/webaudio_visualizer.html可视化音频素材位于 examples/sounds/。适用前提与注意事项需要浏览器支持 Web Audio APIAudioContext首次创建通常要在用户手势点击等之后才能处于running状态因此实际项目应像 examples/webaudio_timing.html 那样先放一个Play按钮再初始化音频只有buffer源支持完整播放控制audioNode / mediaNode / mediaStreamNode源的播放时序由源节点自身管理hasPlaybackControl falseloopStart/loopEnd/offset/duration均以秒为单位且在play()建源时才生效——如需在播放中动态改变循环区间应先pause()再修改并重新play()每个Audio实例持有独立GainNode并永久连接在监听器输入上长期创建大量实例会累积节点建议复用实例而非反复 new。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表