溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

前端音頻可視化Web?Audio如何實現(xiàn)

發(fā)布時間:2023-03-09 13:49:11 來源:億速云 閱讀:134 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“前端音頻可視化Web Audio如何實現(xiàn)”的相關(guān)知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“前端音頻可視化Web Audio如何實現(xiàn)”文章能幫助大家解決問題。

實現(xiàn)思路

首先畫肯定是用canvas去畫,關(guān)于音頻的相關(guān)數(shù)據(jù)(如頻率、波形)如何去獲取,需要去獲取相關(guān)audio的DOM 或通過請求處理去拿到相關(guān)的音頻數(shù)據(jù),然后通過Web Audio API 提供相關(guān)的方法來實現(xiàn)。(當(dāng)然還要考慮要音頻請求跨域的問題,留在最后。)

一個簡單而典型的 web audio 流程如下(取自MDN):

  • 創(chuàng)建音頻上下文

  • 在音頻上下文里創(chuàng)建源 &mdash; 例如 <audio>, 振蕩器,流

  • 創(chuàng)建效果節(jié)點,例如混響、雙二階濾波器、平移、壓縮

  • 為音頻選擇一個目的地,例如你的系統(tǒng)揚聲器

連接源到效果器,對目的地進(jìn)行效果輸出

前端音頻可視化Web?Audio如何實現(xiàn)

實現(xiàn)

一、頻率圖

實現(xiàn)第一種類型,首先我們需要通過fetch或xhr來獲取一個線上音頻的數(shù)據(jù),這里以fetch為例;

 //創(chuàng)建一個音頻上下文、考慮兼容性問題
 let audioCtx = new (window.AudioContext || window.webkitAudioContext)();
 //添加一個音頻源節(jié)點
 let source = audioCtx.createBufferSource();
//res.arrayBuffer是將數(shù)據(jù)轉(zhuǎn)換為arrayBuffer格式
 fetch(url).then((res) => res.arrayBuffer()).then((res) => {
        //decodeAudioData是將arrayBuffer格式數(shù)據(jù)轉(zhuǎn)換為audioBuffer
        audioCtx.decodeAudioData(res).then((buffer) => {
          // decodeAudioData解碼完成后,返回一個AudioBuffer對象
          // 繪制音頻波形圖
          draw(buffer);
          // 連接音頻源
          source.buffer = buffer;
          source.connect(audioCtx.destination);
          // 音頻數(shù)據(jù)處理完畢
        });
      });

前端音頻可視化Web?Audio如何實現(xiàn)

需要明白的是,source.connect(audioCtx.destination)是將音頻源節(jié)點鏈接到輸出設(shè)備,否則會沒聲音哦。那么現(xiàn)在有了數(shù)據(jù)、我們只需要通過canvas將數(shù)據(jù)畫出來即可。

function draw(buffer) {
  // buffer.numberOfChannels返回音頻的通道數(shù)量,1即為單聲道,2代表雙聲道。這里我們只取一條通道的數(shù)據(jù)
  let data = [];
  let originData = buffer.getChannelData(0);
  // 存儲所有的正數(shù)據(jù)
  let positives = [];
  // 存儲所有的負(fù)數(shù)據(jù)
  let negatives = [];
  // 先每隔50條數(shù)據(jù)取1條
  for (let i = 0; i < originData.length; i += 50) {
    data.push(originData[i]);
  }
  // 再從data中每10條取一個最大值一個最小值
  for (let j = 0, len = data.length / 10; j < len; j++) {
    let temp = data.slice(j * 10, (j + 1) * 10);
    positives.push(Math.max(...temp));
    negatives.push(Math.min(...temp));
  }
  if (canvas.getContext) {
    let ctx = canvas.getContext("2d");
    canvas.width = positives.length;
    let x = 0;
    let y = 75;
    let offset = 0;
    var grd = ctx.createLinearGradient(0, 0, canvas.width, 0);
    // 為漸變添加顏色,參數(shù)1表示漸變開始和結(jié)束之間的位置(用0至1的占比表示),參數(shù)2位顏色
    grd.addColorStop(0, "yellow");
    grd.addColorStop(0.5, "red");
    grd.addColorStop(1, "blue");
    ctx.fillStyle = grd;
    ctx.beginPath();
    ctx.moveTo(x, y);
    // 橫坐標(biāo)上方繪制正數(shù)據(jù),下方繪制負(fù)數(shù)據(jù)
    // 先從左往右繪制正數(shù)據(jù)
    // x + 0.5是為了解決canvas 1像素線條模糊的問題
    for (let k = 0; k < positives.length; k++) {
      ctx.lineTo(x + k + 0.5, y - 50 * positives[k]);
    }
    // 再從右往左繪制負(fù)數(shù)據(jù)
    for (let l = negatives.length - 1; l >= 0; l--) {
      ctx.lineTo(x + l + 0.5, y + 50 * Math.abs(negatives[l]));
    }
    // 填充圖形
    ctx.fill();
  }
}

[參考文章](Web Audio - 繪制音頻圖譜 

二、實時頻率圖

實現(xiàn)第二種類型,獲取實時頻率,用到的API與第一種有區(qū)別,但流程一直,都是通過一個音頻源節(jié)點通過連接達(dá)到效果。只不過在連接的中間加入了一個分析器analyser,在將分析器連接到輸出設(shè)備。

    const audio =document.querySelector('audio')
    //解決音頻跨域問題
    audio.crossOrigin ='anonymous'
    const  canvas =document.querySelector('canvas')
    const ctx=canvas.getContext("2d")
        function initCanvas(){
        //初始化canvas
            canvas.width=window.innerWidth*devicePixelRatio
            canvas.height=(window.innerHeight/2)*devicePixelRatio
        }
        initCanvas()
        //將數(shù)據(jù)提出來
        let dataArray,analyser;
        //播放事件
        audio.onplay=function(){
            //創(chuàng)建一個音頻上下文實例
            const audioCtx=new (window.AudioContext || window.webkitAudioContext)();
            //添加一個音頻源節(jié)點
            const source=audioCtx.createMediaElementSource(audio);
            //分析器節(jié)點
             analyser=audioCtx.createAnalyser();
            //fft分析器  越大 分析越細(xì)
            analyser.fftSize=512
            //創(chuàng)建一個無符號字節(jié)的數(shù)組
             dataArray=new Uint8Array( analyser.frequencyBinCount);
            //音頻源節(jié)點 鏈接分析器
            source.connect(analyser)
            //分析器鏈接輸出設(shè)備
            analyser.connect(audioCtx.destination,)
        }

那么接下來至于怎么把數(shù)據(jù)畫出來,就憑大家的想法了。

            requestAnimationFrame(draw)
            //
            const {width ,height}=canvas;
            ctx.clearRect(0,0,width,height)
            //分析器節(jié)點分析出的數(shù)據(jù)到數(shù)組中
            ctx.fillStyle='#78C5F7'
           ctx.lineWidth = 2;
            ctx.beginPath();
            //getByteFrequencyData,分析當(dāng)前音頻源的數(shù)據(jù) 裝到dataArray數(shù)組中去
            //獲取實時數(shù)據(jù)
            analyser.getByteFrequencyData(dataArray)
            // console.log(dataArray);
            const len =dataArray.length;
            const barWidth=width/len;
            let x=0;
            for(let i=0;i<len;i++){
                const data=dataArray[i];
                const barHeight=data/255*height;
                // ctx.fillRect(x,y,barWidth,height)
        let v = dataArray[i] / 128.0;
        let y = v * height/2;
        if(i === 0) {
            ctx.moveTo(x, y);
        } else {
            ctx.lineTo(x, y);
        }
        x += barWidth;
            }
            // ctx.lineTo(canvas.width, canvas.height/2);
            ctx.stroke();
        }
        draw();

關(guān)于請求音頻跨域問題解決方案

給獲取的audio DOM添加一條屬性即可

   audio.crossOrigin ='anonymous'

或者直接在 aduio標(biāo)簽中 加入 crossorigin="anonymous"

關(guān)于“前端音頻可視化Web Audio如何實現(xiàn)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI