• 使用Web Audio API绘制音波图


    摘要:Web Audio API是对<audio> 标签功能上的补充,我们可以用它完成混音、音效、平移等各种复杂的音频处理,本文简单的使用其完成音波图的绘制。 

    PS:本例子使用ES6编程,最好在新版chrome中运行。

    一、前端文件index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title>audiogram</title>
    </head>
    <body>
    <canvas id="canvas" width="400" height="100"></canvas>
    <button class="play" style=" 50px;height: 50px;">开始</button>
    <button class="stop" style=" 50px;height: 50px;">停止</button>
    </body>
    <script type="module" src="Main.js"></script>
    </html>

    PS:浏览器加载ES6模板使用<script>标签时,要加入type = “module”属性,浏览器会默认异步加载,并知道Main.js是一个ES6模块。

    二、创建Audio类

    //audit类,用于加载,播放音乐
    export class Audio {
    
        //单例
        static getInstance() {
            if (!Audio.instance) {
                Audio.instance = new Audio();
            }
            return Audio.instance;
        }
    
        //构造函数
        constructor() {
            this.ctx = new (window.AudioContext || window.webkitAudioContext)();
        }
    
        //加载资源
        getData() {
            this.analyser = this.ctx.createAnalyser();
            //从元素创建媒体节点 可以直接将audio元素传入后创建,就不用request来请求资源
            //this.source = this.ctx.createMediaElementSource(audio);
            this.source = this.ctx.createBufferSource();
            this.source.loop = true;
            //创建AnalyserNode,用来显示音频时间和频率的数据
            this.source.connect(this.analyser);
            //最后连接到音频渲染设备,发出声音
            this.analyser.connect(this.ctx.destination);
    
            //获取频率
            this.freqs = new Uint8Array(this.analyser.frequencyBinCount);
    
            //请求资源
            let request = new XMLHttpRequest();
    
            request.open('get', 'res/bgm.mp3', true);
    
            //responseType属性须设置为arraybuffer
            request.responseType = 'arraybuffer';
    
            //decodeAudioData方法用于解码音频文件
            request.onload = () => {
                var audioData = request.response;
                this.ctx.decodeAudioData(audioData, (buffer) => {
                    //将解码后的音频文件作为声音的来源
                    this.source.buffer = buffer;
                    //立即开始播放声音
                    this.source.start(0);
                }, (e) => {
                    "Error with decoding audio data" + e.error
                });
            };
    
            request.send();
        }
    
    }

    三、创建Main类

    import {Audio} from "./js/Audio.js";
    
    //用于控制整个页面的流程
     class Main {
        constructor() {
            //获取audio实例
            this.audio = Audio.getInstance();
            this.init();
        }
    
        //初始化
        init() {
            //初始化按钮
            this.play = document.querySelector('.play');
            this.stop = document.querySelector('.stop');
    
            //确保加载完资源后开始输出
            let promise = new Promise((resolve) => {
                this.audio.getData();
                resolve();
            });
            promise.then(() => {
                this.initCanvas();
                this.outPut()
            });
    
            //播放按钮
            this.play.onclick = () => {
                this.audio.ctx.resume();
                this.outPut();
                this.play.setAttribute('disabled', 'disabled');
            }
    
            //停止按钮
            this.stop.onclick = () => {
                this.audio.ctx.suspend();
                //this.audio.source.stop(0);使用stop停止时无法恢复,需要重载资源
                cancelAnimationFrame(this.timer);
                this.play.removeAttribute('disabled');
            }
        }
    
        //初始化canvas
        initCanvas() {
            let cv = document.querySelector('#canvas');
            this.canvasWidth = cv.width;
            this.canvasHeight = cv.height;
            this.canvas = cv.getContext("2d");
            this.canvas.translate(0.5, 0.5);
            this.outPutData = this.audio.freqs;
        }
    
        //输出图像
        outPut() {
            var height = this.canvasHeight;
            var width = this.canvasWidth;
            var outPutData = this.outPutData;
            var length = outPutData.length;
            this.audio.analyser.getByteFrequencyData(outPutData);
            //将缓冲区的数据绘制到Canvas上
            this.canvas.clearRect(-0.5, -0.5, width, height);
            this.canvas.beginPath(), this.canvas.moveTo(0, height);
            for (var i = 0; i < width; i++)
                this.canvas.lineTo(i, height - height * outPutData[Math.round(length * i / width)] / 255);
            this.canvas.lineTo(i, height), this.canvas.fill();
            //请求下一帧
            this.timer = requestAnimationFrame(() => {
                this.outPut()
            });
        }
    }
    new Main();

    PS:在ES6中使用箭头函数时,箭头函数中的this指向Main,真的超级好用。

    注意:使用chrome打开时会出现 Origin 'null' is therefore not allowed access.这是由于在本地html页面ajax请求本地或者局域网server的资源时,被浏览器禁止了,跨域请求会带来安全隐患,因此谷歌浏览器对此做出了限制。

    在服务端我们可以设置response.setHeader("Access-Control-Allow-Origin: *")允许这么做,如果是客户端的话我们可以右键chrome快捷方式—>属性->目标-> 将"--allow-file-access-from-files"添加至最后重启浏览器即可。

    工程目录结构:

    └── audiogram
        ├── js
        │   └── Audio.js
        ├── res   
         │   └── bgm.mp3
         ├ ─ ─   index.html
         └ ─ ─   Main.js
     
    效果图:
  • 相关阅读:
    httpclient_1
    jmeter java请求
    fiddler监听手机
    lr文件的作用?
    配置源
    数据结构--堆
    A + B Problem II 高精度
    最小生成树(kruskal算法)+prim算法
    P3371 【模板】单源最短路径(弱化版)
    P3368 【模板】树状数组 2(实现区间修改&单点查询)
  • 原文地址:https://www.cnblogs.com/sincw/p/8859102.html
Copyright © 2020-2023  润新知