web-dev-qa-db-ja.com

波形を視覚化して操作するためのWebオーディオ

オーディオファイルの波形を表示するJavaScriptプログラムを作成するにはどうすればよいですか? WebAudioとCanvasを使いたい。

私はこのコードを試しました:

(new window.AudioContext).decodeAudioData(audioFile, function (data) {
   var channel = data.getChannelData(0);
   for (var i = 0; i < channel; i++) {
       canvas.getContext('2d').fillRect(i, 1, 40 - channel[i], 40);
   }
});

しかし、結果は私が望むものからはほど遠いです(つまり、長方形で描いているので、画像は滑らかではありません)。この画像のように滑らかに見せたい:

Waveform example

波形を実装する方法に関するヒントはありますか?

22
katspaugh

AudioJedit に興味があるかもしれません。これはオープンソースプロジェクトです GitHubでホストされています 。オーディオファイルをロードするための小さなサーバー側のnode.jsスクリプトがありますが、オーディオとのすべての対話はクライアント側のJavaScriptで実装されています。これはあなたが探しているものと似ていると思います。

9
Vadim Baryshev

結局、自分のライブラリを公開しました:wavesurfer.js

PCMデータから波形を描画し、それをクリックしてオーディオの領域を探します。

Imgur

42
katspaugh

うまくいけば)波形の簡単な使用とアプリとの統合については、IRCAMで何をしているのか、特に波形を確認することをお勧めします。この特定のケースでは。

それはすべてオープンソースであり、モジュール性(および進行中の作業)を目的としています

あなたは ここのデモ を見つけることができます
そして対応する githugリポジトリ

2
vectorsize

レンダリングコードは、オーディオの1秒あたり44100ピクセルをレンダリングするため、非常に非効率的です。できれば、データセットを減らして最大でビューポート幅をレンダリングする必要があります。

ビューポートに波形を合わせるために必要なピクセルごとのサンプル範囲は、audioDurationSeconds * samplerate/viewPortWidthPxで計算できます。したがって、1000pxのビューポートと44100サンプルレートで2秒のオーディオファイルの場合、ピクセルあたりのサンプル数=(2 * 44100)/ 1000 = 〜88です。画面上の各ピクセルについて、そのサンプル範囲から最小値と最大値を取得し、このデータを使用して波形を描画します。

これを行うアルゴリズムの例を次に示しますが、ピクセルごとのサンプルを引数として指定し、スクロール位置を指定して仮想スクロールとズームを可能にします。パフォーマンスのために微調整できる解像度パラメーターが含まれています。これは、ピクセルサンプル範囲ごとに取得するサンプル数を示します。 Javascriptでズーム可能なオーディオ波形タイムラインを描画

そこにあるdrawメソッドはあなたのものと似ていますが、それをスムーズにするには、fillRectの代わりにlineToを使用する必要があります。この違いは実際にはそれほど大きくないはずです。キャンバスの幅と高さの属性を設定するのを忘れているかもしれません。これをcssで設定すると、描画がぼやけます。属性を設定する必要があります。

let drawWaveform = function(canvas, drawData, width, height) {
   let ctx = canvas.getContext('2d');
   let drawHeight = height / 2;

   // clear canvas incase there is already something drawn
   ctx.clearRect(0, 0, width, height);

   ctx.beginPath();
   ctx.moveTo(0, drawHeight);
   for(let i = 0; i < width; i++) {
      // transform data points to pixel height and move to centre
      let minPixel = drawData[i][0] * drawHeigth + drawHeight;
      ctx.lineTo(i, minPixel);
   }
   ctx.lineTo(width, drawHeight);
   ctx.moveTo(0, drawHeight);
   for(let i = 0; i < width; i++) {
      // transform data points to pixel height and move to centre
      let maxPixel = drawData[i][1] * drawHeigth + drawHeight;
      ctx.lineTo(i, maxPixel);
   }
   ctx.lineTo(width, drawHeight);
   ctx.closePath();
   ctx.fill(); // can do ctx.stroke() for an outline of the waveform
} 
0
David Sherman