音频可视化

音频可视化使用了WebAudio的功能,仅在支持WebAudio的平台有效。

import { _decorator, AudioClip, Component, instantiate, Label, lerp, Node, UITransform, v3 } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('MusicView')
export class MusicView extends Component {
    @property(Label)
    tip: Label = null;
    @property([AudioClip]) 
    bgms: AudioClip[] = [];
    @property(Node) root: Node = null;
    audioBufferSourceNode:AudioBufferSourceNode;
    analyser:AnalyserNode;
    dataArray:Uint8Array;

    audioClip: AudioClip = null;
    isPlaying:boolean = false;
    bgm_index:number = 0;
    copyNode:Node = null;
    start() {
        this.audioClip = this.bgms[0];
        this.bgm_index = 0;
        this.tip.string = 'BGM - ' + this.bgm_index; 
        
        this.initAudio();
    }

    initAudio(){
        //取自节点作为克隆对象
        if(!this.copyNode){
            this.copyNode = instantiate(this.root.children[0]);
        }
        //清空root
        this.root.removeAllChildren();
        //示例化 item
        for(let i = 0;i < 23;i++){
            let item = instantiate(this.copyNode);
            this.root.addChild(item);
            item.setPosition(v3(-220 + i * 20, 0, 0))
        }

    }

    changeBgm(){
        this.bgm_index += 1;
        if(this.bgm_index >= this.bgms.length){
            this.bgm_index = 0;
        };
        this.audioClip = this.bgms[this.bgm_index];
        this.tip.string = 'BGM - ' + this.bgm_index; 
        this.initAudio();
        if(this.isPlaying){
            this.audioBufferSourceNode.stop();
            this.makeAnalyser();
        }
    }

    makeAnalyser(){
        let AudioContext = window.AudioContext;
        // audioContext 只相当于一个容器。
        let audioContext = new AudioContext();
        // 要让 audioContext 真正丰富起来需要将实际的音乐信息传递给它的。
        // 也就是将 AudioBuffer 数据传递进去。
        // 以下就是创建音频资源节点管理者。
        this.audioBufferSourceNode = audioContext.createBufferSource();
        this.audioBufferSourceNode.loop = true;
        // 将 AudioBuffer 传递进去。这里 cocos 封装的比较深
        const audioBuffer = (this.audioClip as any)._player._player._audioBuffer;
        this.audioBufferSourceNode.buffer = audioBuffer;
        // 创建分析器。
        this.analyser = audioContext.createAnalyser();
        // 精度设置
        this.analyser.fftSize = 256;
        // 在传到扬声器之前,连接到分析器。
        this.audioBufferSourceNode.connect(this.analyser);
        // 连接到扬声器。
        this.analyser.connect(audioContext.destination);
        // 开始播放
        this.audioBufferSourceNode.start(0);
    }
    onClickPlayBtn() {
        if (this.isPlaying) {
            // 停止播放
            this.audioBufferSourceNode.stop();
            this.isPlaying = false;
        } else {
            this.makeAnalyser();
            this.isPlaying = true;
        }
    }
    update(deltaTime: number) {
        // 等待准备好
        if (!this.analyser) return;
        // 建立数据准备接受数据
        this.dataArray = new Uint8Array(this.analyser.frequencyBinCount);
        // 分析结果存入数组。
        this.analyser.getByteFrequencyData(this.dataArray);
        this.draw(this.dataArray);
        
    }
    draw(dataArray: Uint8Array) {
        // 精度 256 解析数组长度为一半 128 长,这里我根据音乐挑选展示 60 个数据
        for (let i = 0; i < 30; i++) {
            // 在 10 - 70 每隔 2 个取音频数据展示,数据大小乘以 2 作为节点高度
            let h = dataArray[10 + i * 2] * 2;
            if (h < 10) h = 10;
            let node = this.root.children[i];
            // 插值手段,数据以 0.4 为阶段逼近目标值,高度变的不那么生硬
            const height = node.getComponent(UITransform).height;
            node.getComponent(UITransform).height = lerp(height, h, 0.4);
        }
        
    }
    checkEnd(){
        //if(this.audioBufferSourceNode.loop = true;)
    }
}


posted @ 2024-12-16 14:05  EricShx  阅读(12)  评论(0)    收藏  举报