Threejs进阶之十六:音频可视化

news/2024/12/2 22:51:48/

最近事情比较多,博客更新的有点慢了,今天更新一期,主要聊一聊通过Threejs提供的音频API实现音频的可视化效果,先看下最终实现的效果

音频可视化

目录

  • Threejs中音频相关的类
    • Audio 类
      • 构造函数
      • 常用属性
      • 常用方法
      • 创建Audio对象示例
    • AudioListener 类
      • 构造函数
      • 常用属性
      • 常用方法
      • 创建AudioListener对象示例
    • AudioContext类
      • 常用方法
    • PositionalAudio 类
      • 构造函数
      • 常用属性
      • 常用方法
      • 创建PositionalAudio对象示例
    • AudioAnalyser 类
      • 构造函数
      • 常用属性
      • 常用方法
      • 创建AudioAnalyser对象示例
    • AudioLoader 类
      • 构造函数
      • 属性
      • 常用方法
      • 创建AudioLoader对象示例
  • 用Threejs提供的音频类实现一个音频可视化效果

Threejs中音频相关的类

在Three.js中,音频功能是通过Web Audio API实现的。Web Audio API可以生成、控制和处理音频,要实现音频的可视化效果,我们需要先来了解下Threejs给我们提供的音频相关的类,Threejs给我们提供的音频相关的类主要包括Audio、AudioListener、PositionalAudio、AudioAnalyser和AudioLoader这几个类。

Audio 类

Audio类用于创建一个全局的audio对象,表示一个音频源,在Three.js中用于播放音频和控制音频参数。

构造函数

Audio( listener : AudioListener )
其中:listener参数是一个AudioListener对象,用于监听音频的播放

常用属性

  • autoplay:布尔值,指定音频是否自动播放;
  • context:Web Audio API的AudioContext对象,表示Audio对象所处的一个音频环境。
  • gain:AudioParam对象,用于控制音频的音量或增益。
  • duration:音频的时长,以秒为单位。
  • source:AudioBufferSourceNode对象,用于控制音频的播放、停止等;

常用方法

  • setBuffer():设置音频源的数据。设置source给audioBuffer, 和设置sourceType给’buffer’。
  • setLoop():设置音频是否循环播放
  • setVolume():设置音频源的音量
  • .hasPlaybackControl:是否可以使用 play(), pause()等方法控制播放. 默认为 true
  • .play(delay ):如果hasPlaybackControl是true,播放音频源
  • .stop () : 如果hasPlaybackControl是true, 停止播放
  • .pause () : 如果hasPlaybackControl是true, 暂停播放
  • .setMediaElementSource ( mediaElement ) : 应用传入的HTMLMediaElement类型对象作为音源。并且设置hasPlaybackControl为false。
  • .setMediaStreamSource ( mediaStream ) : 应用传入的MediaStream类型对象作为音源。并且设置hasPlaybackControl为false。

创建Audio对象示例

创建Audio对象需要传入一个AudioListener对象作为参数, 所以,在创建Audio对象前,需要先创建一个AudioListener 对象

// 创建一个 AudioListener 并将其添加到 camera 中
const listener = new THREE.AudioListener()
camera.add( listener )
// 创建一个全局 audio 源
const sound = new THREE.Audio( listener )

AudioListener 类

AudioListener 类是Three.js中用于监听音频的类,用一个虚拟的listener表示在场景中所有的位置和非位置相关的音效。它负责处理场景中所有3D音频源的音量、音调、距离效果等。主要作用是使用户能够模拟3D空间中的音频效果。
一个three.js程序通常创建一个AudioListene。它是音频实体构造函数的必须参数。listener对象是camera的子对象.。Camera的3D变换表示了listener的3D变换。

构造函数

AudioListener的构造函数没有参数,它创建了一个用于监听音频的对象。
AudioListener( ):创建一个新的AudioListener。

常用属性

  • context:Web Audio API的AudioContext对象,表示AudioListener对象所处的一个音频环境。
  • gain:对整个场景的音量或增益进行控制。

常用方法

  • setMasterVolume(volume):设置所有音频的主音量大小,volume为0-1的数值。
  • getMasterVolume():获取当前所有音频的主音量大小。

创建AudioListener对象示例

cosnt listener = new THREE.AudioListener()
camera.add( listener )

上述代码创建了一个用于监听音频的listener对象,使其随着相机一起移动。然后,可以通过调用listener的setMasterVolume()方法来设置所有音频的音量大小

AudioContext类

AudioContext是Web Audio API中的音频上下文环境,用于处理音频数据。在Three.js中,AudioContext类用于创建一个用于处理音频的上下文环境,可以用于创建和控制音频节点。在AudioListener和AudioLoader 类中被使用。

常用方法

  • .getContext () : AudioContext类型;如果定义了,返回给外部context的值, 否则创建一个新的AudioContext。
  • .setContext ( value : AudioContext ) : AudioContext类型;外部用来设置 context 的值。

PositionalAudio 类

PositionalAudio 类用于创建一个位置相关的音频对象。
PositionalAudioThree.js中用于实现3D空间音效的音频源类,它继承自THREE.Audio对象,并添加了音源在3D空间中的位置、方向、距离效果等属性。主要作用是使用户能够在Three.js场景中实现3D声音效果。用于使音效根据360度方向自适应,同时还可以调整立体声效果。

构造函数

PositionalAudio( listener : AudioListener )
PositionalAudio的构造函数需要传入一个AudioListener类型的listener参数,表示用于监听音频的对象。这个listener参数是必须的

常用属性

  • panner:控制每个PositionalAudio对象的定位效果的PannerNode对象。
    PositionalAudio类继承自Audio类,所以也继承了Audio类的属性。

常用方法

  • setRefDistance(value):设置panner.refDistance的值,即设置参考距离,value为距离大小。
  • setRolloffFactor(value):设置panner.rolloffFactor的值,即设置衰减系数,value为衰减系数大小。
  • setDistanceModel(value):设置panner.distanceModel的值,即设置距离模型,value可以为"linear"、“inverse"或"exponential”。
  • .setDirectionalCone (coneInnerAngle, coneOuterAngle, coneOuterGain) : 这个方法用来把环绕声音转换为定向声音directional sound。
    另外,PositionalAudio类也继承了Audio类的方法。

创建PositionalAudio对象示例

const listener = new THREE.AudioListener()
camera.add( listener )
const sound = new THREE.PositionalAudio( listener )
const audioLoader = new THREE.AudioLoader()
audioLoader.load( 'sounds/song.ogg', function( buffer ) {sound.setBuffer( buffer )sound.setRefDistance( 20 )sound.play()
})

AudioAnalyser 类

AudioAnalyser 类是Three.js中用于对音频数据进行分析的类,它可以分析音频数据的频域和时域信息,并对其进行可视化。主要作用是实现音频可视化效果,如频谱条形图、音量大小等。
AudioAnalyser对象,使用AnalyserNode 去分析音频数据。
AnalyserNode
AnalyserNode是一个Web Audio API。
AnalyserNode接口表示能够提供实时频率和时域分析信息的节点。它是一个AudioNode,它将音频流从输入传递到输出,但不改变,但允许您获取生成的数据,对其进行处理,并创建音频可视化。

构造函数

AudioAnalyser( audio, fftSize )
AudioAnalyser的构造函数需要传入一个audio参数,表示用于分析的音频源,并且可以通过fftSize(Fast Fourier Transform size)参数来指定频域分析的精度。
简单介绍下 fftSize
AnalyserNode接口的fftSize属性是一个无符号长值,表示执行快速傅立叶变换(FFT)以获取频域数据时使用的样本中的窗口大小。

常用属性

  • analyser:AnalyserNode对象,用于获取音频数据并进行分析。
  • fftSize:FFT(Fast Fourier Transform)的缓冲区大小。Integer类型,2的幂次方最高为2048, 用来表示确定频域的FFT (傅立叶变换)大小。

常用方法

  • getAverageFrequency():获取音频数据的平均频率。
  • getFrequencyData():获取音频数据的频域信息。

创建AudioAnalyser对象示例

const analyser = new THREE.AudioAnalyser( sound, 32 )
const data = analyser.getAverageFrequency()

AudioLoader 类

AudioLoader是Three.js中用于加载音频文件的类,它可以加载多种格式的音频文件,并将音频数据转换为AudioBuffer对象,供Audio对象使用。主要作用是实现对音频文件的加载和转换。 内部默认使用FileLoader来加载文件

构造函数

AudioLoader( manager : LoadingManager ):创建一个新的AudioLoader
参数manager是加载器使用的loadingManager。默认为THREE.DefaultLoadingManager。

属性

AudioLoader 的属性继承自Loader

常用方法

.load ( url : String, onLoad : Function, onProgress : Function, onError : Function ) : 从URL中进行加载并将已经加载的AudioBuffer传递给onLoad。
.load方法的参数
url — 音频文件的URL或者路径,也可以为 Data URI。
onLoad — 加载完成(成功)时将调用。回调参数为将要加载的响应文本。可选参数
onProgress — 将在加载过程中进行调用。参数为XMLHttpRequest实例,实例包含total和loaded字节。可选参数
onError — 在加载错误时被调用。可选参数

创建AudioLoader对象示例

// 初始化一个监听
const audioListener = new THREE.AudioListener()
// 把监听添加到camera
camera.add( audioListener )
// 初始化音频对象
const oceanAmbientSound = new THREE.Audio( audioListener )
// 添加一个音频对象到场景中
scene.add( oceanAmbientSound )
// 初始化一个加载器
const loader = new THREE.AudioLoader()
// 加载资源
loader.load(// 资源URL'audio/dh.mp3',// onLoad回调 可选参数function ( audioBuffer ) {// 给一个加载器对象设置音频对象的缓存oceanAmbientSound.setBuffer( audioBuffer );// 播放音频oceanAmbientSound.play();},// onProgress回调 可选参数function ( xhr ) {console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );},// onError回调 可选参数function ( err ) {console.log( 'An error happened' );}

用Threejs提供的音频类实现一个音频可视化效果

下面我们用上面介绍的类实现一个音频可视化的效果
创建音频可视化效果的主要有以下步骤
1、引入Threejs
2、初始化场景、相机、渲染器
3、创建音频监听器
4、创建Audio对象
5、创建音源文件变量,存储本地音乐文件
6、创建浏览器的Audio对象,并将上面的音源文件作为参数传入
7、创建AudioAnalyser对象,使用AnalyserNode 去分析音频数据
8、自定义Shader,并创建数据贴图,将上面的音频文件作为贴图传入
9、创建可视化对象
10、更新可视化对象
完整示例代码如下:

<template><div id="overlay"><button id="startButton" @click="startPlay">Play</button></div><div class="container"></div>
</template>
<script setup>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
let scene, camera, renderer, analyser, uniforms;
function startPlay() {init()
}
function init() {const fftSize = 2048;const overlay = document.getElementById('overlay');overlay.remove();const container = document.querySelector('.container');const width = window.innerWidthconst height = window.innerHeightrenderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(width, height);renderer.setClearColor(0x000000);renderer.setPixelRatio(window.devicePixelRatio);container.appendChild(renderer.domElement);scene = new THREE.Scene();camera = new THREE.Camera();// 创建音频监听器const listener = new THREE.AudioListener();// 创建Audio对象const audio = new THREE.Audio(listener);// 创建音源文件const soundSource = '/mf.mp3'// 创建HTML的HTMLAudioElementconst mediaElement = new Audio(soundSource) //注意这里的Audio是浏览器的对象,不是Threejs的mediaElement.play()audio.setMediaElementSource(mediaElement) //注意这里的audio才是上面Three创建的THREE.Audio()对象// 创建AudioAnalyser对象,使用AnalyserNode 去分析音频数据analyser = new THREE.AudioAnalyser(audio, fftSize)const format = (renderer.capabilities.isWebGL2) ? THREE.RedFormat : THREE.LuminanceFormat;/* 这行代码主要是用于确定渲染器使用的颜色格式(color format)是THREE.RedFormat还是THREE.LuminanceFormat。根据渲染器的WebGL上下文(context)是否是WebGL2版本来决定使用哪种颜色格式。其目的是根据WebGL上下文的版本选择更加合适的颜色格式,在渲染时可以提高性能和效果。- 如果WebGL上下文是WebGL2,则使用THREE.RedFormat,表示使用R(红)通道作为颜色信息,没有G(绿)和B(蓝)通道。- 如果WebGL上下文不是WebGL2,则使用THREE.LuminanceFormat,表示使用亮度(Luminance)值作为颜色信息(通常是灰度),也就是只有一个通道。 */uniforms = {tAudioData: { value: new THREE.DataTexture(analyser.data, fftSize / 2, 1, format) },/*** analyser.data表示音频数据源,它是一个一维数组,包含着频域数据; fftSize / 2表示频域采样点个数;1表示纹理在y轴方向上的采样点个数,因为它只包含一行数据;format表示纹理数据类型,通常是THREE.LuminanceFormat或THREE.RGBAFormat。*/iResolution: { value: new THREE.Vector2(renderer.domElement.width, renderer.domElement.height) },iTime: { value: 0 },soundTextureYOffset: { value: 0 },soundTextureHeight: { value: 5 },soundTextureWidth: { value: 5 },uvScale: { value: new THREE.Vector2( 1, 1 ) },};const material = new THREE.ShaderMaterial()material.uniforms = uniformsmaterial.vertexShader = `uniform vec2 uvScale;varying vec2 vUv; void main() { vUv =  uv;gl_Position = vec4( position, 2.0 );}`material.fragmentShader = ` //Math constants#define PI 3.14159#define TWO_PI 6.28318//Frequency range to which the halo reacts currently set to 0-520hz#define FREQ 512.0uniform vec2      iResolution;           // viewport resolution (in pixels)uniform float     iTime;                 // shader playback time (in seconds)uniform sampler2D tAudioData;uniform float     soundTextureYOffset;uniform float     soundTextureHeight;uniform float     soundTextureWidth;varying vec2 vUv;vec3 hsv2rgb(vec3 c) {vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);}float luma(vec3 color) {return dot(color, vec3(0.299, 0.587, 0.114));}float getSound(float x){vec2 tAudioDataUV = vec2((x + iTime) / soundTextureWidth, soundTextureYOffset / soundTextureHeight);return texture2D(tAudioData, tAudioDataUV).r;} void main(){vec2 uv = vUv;vec2 uvn = uv * 2.0 - 1.0;uvn.x *= iResolution.x / iResolution.y;float angle = atan(normalize(uvn).x, normalize(uvn).y);float angleNormalized = angle / PI;float inner_halo = max(pow(1.0 - abs(length(uvn) - 0.6), 8.0), 0.0) * 2.0;inner_halo *= inner_halo;float outer_halo = max(pow(abs(length(uvn)), 4.0), 0.0) * 0.25;float halo = inner_halo + outer_halo;float freq = getSound(abs(angleNormalized) * 2.0) * 2.0; // Scale sound by 2.0 to match the original effectvec3 col = halo * hsv2rgb(vec3(angleNormalized * 0.5 + 0.5 - iTime * 0.1, 1.0, 1.0)) * freq;col = pow(col, vec3(2.0));col += max(length(col) / sqrt(3.0) - 1.0, 0.0);gl_FragColor = vec4(col, 1.0);}`const geometry = new THREE.PlaneGeometry(5, 5) const mesh = new THREE.Mesh(geometry, material);scene.add(mesh);window.addEventListener('resize', onWindowResize);animate();
}
function onWindowResize() {renderer.setSize(window.innerWidth, window.innerHeight);
}function animate() {requestAnimationFrame(animate);render();
}
function render() {analyser.getFrequencyData();uniforms.tAudioData.value.needsUpdate = true;uniforms.iTime.value += 0.0001;renderer.render(scene, camera);
}
</script>
<style scoped>
#overlay {position: absolute;font-size: 16px;z-index: 2;top: 0;left: 0;width: 100%;height: 100%;display: flex;align-items: center;justify-content: center;flex-direction: column;background: rgba(0, 0, 0, 0.7);
}#overlay button {background: transparent;border: 0;border: 1px solid rgb(255, 255, 255);border-radius: 4px;color: #ffffff;padding: 12px 18px;text-transform: uppercase;cursor: pointer;
}
</style>

上面代码有详细注释,这里不再赘述,今天就先到这里吧,喜欢的小伙伴点赞关注加收藏哦!


http://www.ppmy.cn/news/100334.html

相关文章

RPC原理与实现

rpc叫做远程过程调用&#xff0c;是指一台机器上的服务通过通信协议调用网络中另一台机器上的程序&#xff0c;并拿到结果。 1、基本流程 基本流程为&#xff1a; 客户端程序通过Client Stub调度rpc函数Client Stub将调用方法、参数按照通信协议序列化成网络二进制数据&#…

IT公司的吉祥“树” 二叉树-(堆)C语言创建

目录 &#x1f36a;前言 一、树概念及结构 ✅基本概念 ✅树的专有名词 ✅ 树的表示 &#x1f6a9;孩子兄弟表示法 二、二叉树概念及结构 ✅概念 &#x1f60d;&#x1f60d;现实中的二叉树&#xff08;又称IT公司的吉祥物&#xff09;&#x1f60d;&#x1f60d; ✅…

django+python协同过滤推荐算法网上购物商城系统的n9u33

本毕业设计的内容是设计实现一个基于 Django框架的智能推荐算法。它是以 Python语言&#xff0c;MYSQL为数据库开发平台&#xff0c;Tomcat网络信息服务作为应用服务器。智能推荐算法的功能已基本实现&#xff0c;主要包括用户、商品分类、购物商品、订单等。本项目软件架构选择…

基于QT C++封装微软开源的edge-TTS

微软Edge TTS是一种先进的语音合成技术&#xff0c;它能够将文本转换为自然流畅的语音。该技术基于深度学习和人工智能技术&#xff0c;能够模拟人类语音的音调、语速、语调和情感&#xff0c;使得合成的语音听起来非常自然。 微软edge-tts项目地址&#xff1a;GitHub - rany2…

AHB-to-APB Bridge——08burst_test(rdy、nrdy、slverr、tight)、地址

-------------- burst_test:与single_test不同的是&#xff0c;需要在run_phase中使用fork join 让AHB侧和APB侧同时工作&#xff08;不能等AHB都发完APB才工作&#xff09;&#xff1b;num_apb_seq为APB已传输的个数&#xff0c;当APB侧传输数据的个数&#xff0c;大于或等于A…

【Linux】Pinctrl具体框架——Linux学习笔记

简介 Linux Pinctrl &#xff08;Pin control&#xff09;是一个子系统&#xff0c;允许开发者控制芯片引脚的复用、电气属性和其他相关设置。在Linux内核中&#xff0c;Pinctrl是一个重要的组件&#xff0c;提供了硬件抽象层&#xff0c;让开发者在进行驱动程序开发时可以更加…

深入理解Java虚拟机:JVM高级特性与最佳实践-总结-13

深入理解Java虚拟机&#xff1a;JVM高级特性与最佳实践-总结-13 Java内存模型与线程Java内存模型原子性、可见性与有序性先行发生原则 Java内存模型与线程 Java内存模型 原子性、可见性与有序性 Java内存模型是围绕着在并发过程中如何处理原子性、可见性和有序性这三个特征来…

多线程基础知识

( 1 ) 传统使用类Thread和接口Runnable实现 1. 在Thread子类覆盖的run方法中编写运行代码 方式一 new Thread(){ Override public void run(){ while(true){ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }.star…