タイトル通りの内容です。
JavaScriptネタなので、後付けで JavaScript のアドベントカレンダーの 23日目に登録をしてみたりもしました。
さくっと可視化を試す
ソースコードを探す
サンプルは探せばいろいろありそうでしたが、とりあえずサクッと試せそうで分かりやすそうなものを選んでみました。JavaScript関連でよくお世話になる MDN の以下のページのものをそのまま利用できました。
●AnalyserNode.getFloatFrequencyData() - Web APIs | MDN
https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/getFloatFrequencyData
ほぼサンプルそのまま、という利用方法です。ただし注意点があるので補足します。
まずは、以下が手を加える前のサンプルです。
<!doctype html>
<body>
<script>
const audioCtx = new AudioContext();
//Create audio source
//Here, we use an audio file, but this could also be e.g. microphone input
const audioEle = new Audio();
audioEle.src = 'my-audio.mp3';//insert file name here
audioEle.autoplay = true;
audioEle.preload = 'auto';
const audioSourceNode = audioCtx.createMediaElementSource(audioEle);
//Create analyser node
const analyserNode = audioCtx.createAnalyser();
analyserNode.fftSize = 256;
const bufferLength = analyserNode.frequencyBinCount;
const dataArray = new Float32Array(bufferLength);
//Set up audio node network
audioSourceNode.connect(analyserNode);
analyserNode.connect(audioCtx.destination);
//Create 2D canvas
const canvas = document.createElement('canvas');
canvas.style.position = 'absolute';
canvas.style.top = 0;
canvas.style.left = 0;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);
const canvasCtx = canvas.getContext('2d');
canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
function draw() {
//Schedule next redraw
requestAnimationFrame(draw);
//Get spectrum data
analyserNode.getFloatFrequencyData(dataArray);
//Draw black background
canvasCtx.fillStyle = 'rgb(0, 0, 0)';
canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
//Draw spectrum
const barWidth = (canvas.width / bufferLength) * 2.5;
let posX = 0;
for (let i = 0; i < bufferLength; i++) {
const barHeight = (dataArray[i] + 140) * 2;
canvasCtx.fillStyle = 'rgb(' + Math.floor(barHeight + 100) + ', 50, 50)';
canvasCtx.fillRect(posX, canvas.height - barHeight / 2, barWidth, barHeight / 2);
posX += barWidth + 1;
}
};
draw();
</script>
</body>
動作させるために必要なこと(2つ)
Web上から利用可能な音声ファイルをダウンロードしてきて、「my-audio.mp3」という名前にリネームをして、上記のサンプルを動かそうとしたところ動作せず、以下のメッセージがコンソールに表示されていました。
少し補足すると、サンプルコードを適当に「audio.html」などという名前で保存し、ブラウザにそのままドラッグ&ドロップして動かそうとしていました。
ローカルでサーバを動作させる
上記の解決策について説明します。
対応が必要な内容の 1つは、ローカルでサーバを動作させて HTMLファイルにアクセスする形にすることです。手軽に試すには、以下のどれかを使うのが個人的にはオススメです。今回は、Python のもの(python -m SimpleHTTPServer
)を使いました。
●ワンライナーWebサーバを集めてみた - Qiita
https://qiita.com/sudahiroshi/items/e74d61d939f18779970d
ユーザのアクションをトリガーにして音源を再生させる
もう 1つは、コンソールのメッセージに出ていた URL にアクセスした先、以下のページに書かれた内容に対応することです。
●Autoplay Policy Changes | Web | Google Developers
https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#webaudio
大まかに言うと、ユーザのアクションが行われてから処理を行う、というものです。以前から、スマホだと自動再生がされてしまわないようにこのような挙動をさせる仕様があったのは把握していたのですが、デスクトップ版のブラウザ(今回は Mac上で Chrome を利用)でも同様の状況になっていたとは・・・。
とりあえず、画面をクリックしたら初回のみサンプルのコードにあった処理を実行する形に書きかえました。以下がソースコードです。
<!DOCTYPE html>
<body>
<script>
let isStarted = false;
document.documentElement.addEventListener("mousedown", function () {
if (!isStarted) {
isStarted = true;
mouse_IsDown = true;
console.log("mouse down");
const audioCtx = new AudioContext();
//Create audio source
//Here, we use an audio file, but this could also be e.g. microphone input
const audioEle = new Audio();
audioEle.src = "my-audio.mp3"; //insert file name here
audioEle.autoplay = true;
audioEle.preload = "auto";
const audioSourceNode = audioCtx.createMediaElementSource(audioEle);
//Create analyser node
const analyserNode = audioCtx.createAnalyser();
analyserNode.fftSize = 256;
const bufferLength = analyserNode.frequencyBinCount;
const dataArray = new Float32Array(bufferLength);
//Set up audio node network
audioSourceNode.connect(analyserNode);
analyserNode.connect(audioCtx.destination);
//Create 2D canvas
const canvas = document.createElement("canvas");
canvas.style.position = "absolute";
canvas.style.top = 0;
canvas.style.left = 0;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);
const canvasCtx = canvas.getContext("2d");
canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
function draw() {
//Schedule next redraw
requestAnimationFrame(draw);
//Get spectrum data
analyserNode.getFloatFrequencyData(dataArray);
//Draw black background
canvasCtx.fillStyle = "rgb(0, 0, 0)";
canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
//Draw spectrum
const barWidth = (canvas.width / bufferLength) * 2.5;
let posX = 0;
for (let i = 0; i < bufferLength; i++) {
const barHeight = (dataArray[i] + 140) * 2;
canvasCtx.fillStyle =
"rgb(" + Math.floor(barHeight + 100) + ", 50, 50)";
canvasCtx.fillRect(
posX,
canvas.height - barHeight / 2,
barWidth,
barHeight / 2
);
posX += barWidth + 1;
}
}
draw();
}
});
</script>
</body>
実際に動作させた様子
こちらが、実際に動作させた時の画面です。
久しぶりに Web Audio API を触った。
— you (@youtoy) December 29, 2020
以下のサンプルにちょっとだけ手を加えたもの。それと、音源は「効果音ラボ」にあったピアノの音のファイルより。
●AnalyserNode.getFloatFrequencyData() - Web APIs | MDN
https://t.co/Xrts0XTTLC pic.twitter.com/HqQkxRBm48
音声ファイルの入手元
なお今回用いた音源は、よくお世話になっているサイト「効果音ラボ」にあるものを使わせていただきました。