獲取錄音資訊 (getUserMedia)

當我們會使用音訊播放並且會製成頻譜圖之後,接著就要來玩玩錄音了,原理大概就是透過電腦的麥克風,獲取外部聲音,然後經由 Web Audio 聲音處理節點把聲音數位化,更進一步地把聲音儲存下來。( 不過錄音需要用到 https,畢竟如果隨隨便便就可以錄,還真是滿危險的...XD )

如果有遇到出現這個畫面,按下「進階」,點選「繼續前往 www.oxxostudio.tw 網站 (不安全)」即可,會出現這個畫面主要原因就是錄音需的網址要有 SSL 憑證,而我的部落格卻沒有買憑證所以是 http 開頭,才會有這個畫面出現,所以,要試用就只能先這樣囉~ ( 不過如果跑在本機端 localhost 就沒這問題 )

獲取錄音資訊

HTML 先放上兩個按鈕,一個是開始,一個是暫停。

<button id="p">play</button>
<button id="s">pause</button>


然後因為我們錄音會動用到電腦的麥克風,所以要先看看瀏覽器有沒有支援,第一行是有沒有支援 Web Audio,第二行則是要確認可以支援使用者的 media 也就是視訊或麥克風的 API。( 不過現在視訊應該有安全性的限制,只有 https 能存取 )

window.AudioContext = window.AudioContext || window.webkitAudioContext;
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;


getUserMedia這個 API 用法就像下面這樣,constraints是一個物件 ( 可以參考 MDN MediaDevices.getUserMedia() ),可以設定 audio 或 video 的 true 或 false,successCallbackerrorCallback就是成功和失敗時要做的事情。

navigator.getUserMedia(constraints, successCallback, errorCallback);


知道用法後,就可以來寫一下把麥克風收到的聲音轉換為數值陣列的程式,開始一樣先宣告一些變數,然後把剛剛的getUserMediaAPI 放進來。

var s = document.getElementById('s');
var p = document.getElementById('p');
var timer;
var context = new AudioContext();
navigator.getUserMedia({audio: true}, function(stream) {

    //把要做的事情寫在這邊

}, function(){
  console.log('error');
});


接著就把要做的事情寫在裡面,先宣告一個 microphone 變數來用音訊處理器承接麥克風的音訊 ( createMediaStreamSource ),因為進來的都是即時的,所以用 analyser 來做處理,把音訊接到 analyser 上面後就做一些基本的設定,每 20ms 分析一次,完成後打開網頁 console,對著麥克風講話,應該就可以看到數值開始跑囉!不過這裡會把analyser.connect(context.destination);這段給移除掉,因為如果一邊錄音又一邊播放,就會產生無窮迴圈,喇叭就會發出非常刺耳的聲音。 ( 範例:web-audio-recorder-demo01.html )

var microphone = context.createMediaStreamSource(stream);
var analyser = context.createAnalyser();
microphone.connect(analyser);
analyser.connect(context.destination);

analyser.fftSize = 2048;
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Uint8Array(analyser.fftSize);
//analyser.getByteFrequencyData(dataArray);

s.onclick = function(){
  clearTimeout(timer);
};

p.onclick = function(){
  update();
};

update();

function update(){
  console.log(dataArray);
  analyser.getByteFrequencyData(dataArray);
  timer = setTimeout(update,20);
}

獲取錄音資訊

當然只有數值一定不有趣,把數值轉換成視覺化的頻譜才特別,和之前畫頻譜都類似,這裏用比較簡單的方法 ( 不用 SVG、D3 或 Canvas ),純粹先用 div 來表現,首先在畫面裡用 JavaScript 放入 256 個 div,讓 div 依據陣列數值變化高度和背景顏色。( 範例:web-audio-recorder-demo02.html )

獲取錄音資訊

除了把麥克風擷取的聲音化作頻譜外,有沒有方法可以把聲音儲存下來呢?其實是有的,這裏要來介紹一隻 JavaScript:Recorderjs ( https://github.com/mattdiamond/Recorderjs ),他可以直接幫我們把錄製到的聲音變成 wav 的形式儲存下來,為什麼要用這隻程式呢?其實 Web audio 也是可以儲存,但原始的儲存方式仍然受到 https 的影響,而這隻 js 就是幫我們處理掉這些惱人的過程,既然是 MIT 的 opensource,就直接用囉!

獲取錄音資訊

Recorder.js 有幾個 API 可以用,分別是record()負責錄音,stop()停止錄音,clear()清除錄音緩衝區暫存,exportWAV()產生 wav 檔案,修改上面兩個例子,寫法大概就像下面這樣,按下「錄音」按鈕的時候,先 new 一個 Recoder 物件,物件錄製的音訊就是我們剛剛用聲音處理器截取的 microphone,接著就用record()來錄音,停止錄音的按鈕一開始先把計時器停下來,然後用stop()停止錄音,並且產生 wav 檔案以及 wav 檔案的下載連結,然後clear()清除錄音緩衝區暫。

s.onclick = function(){
  clearTimeout(timer);
  p.disabled = false;
  s.disabled = true;
  recorder.stop();
  createDownloadLink();
  recorder.clear();
};

p.onclick = function(){
  recorder = new Recorder(microphone);
  recorder.record();
  p.disabled = true;
  s.disabled = false;
  update();
};


產生 wav 檔案的函式如下,使用exportWAV來產生一個 wav 物件,接著在畫面上用createElement創造一個 audio 標籤來裝載這個 wav 物件,就可以用 HTML5 的播放器來播放,不過因為要下載,所以這裡用了一個叫做createObjectURL的 Web API,目的在幫我們產生一個物件的連結 ( 可以參考 MDN URL.createObjectURL() ),這樣就可以有下載連結下載囉!

function createDownloadLink(){
  recorder.exportWAV(function(blob) {
    var url = URL.createObjectURL(blob);
    var li = document.createElement('li');
    var au = document.createElement('audio');
    var hf = document.createElement('a');

    au.controls = true;
    au.src = url;
    hf.href = url;
    hf.download = new Date().toISOString() + '.wav';
    hf.innerHTML = hf.download;
    li.appendChild(au);
    li.appendChild(hf);
    recordingslist.appendChild(li);
  });
}


完成後,打開網頁,按下錄音就可以開始錄音,同時也會看到聲音頻譜在跳動,錄音完成就會自動產生播放器試聽與下載聲音檔案,相當有趣味的啦!( 範例:web-audio-recorder-demo03.html )

獲取錄音資訊