使用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="width: 50px;height: 50px;">開始</button>
<button class="stop" style="width: 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
 
效果圖:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM