波形データを取得してすべてのデータポイントをレンダリングすることはできません。これは非常に非効率的です。
変数の説明:
- width:描画領域の幅(ピクセル単位)。maxは画面の幅です。
- 高さ:幅と同じですが、描画領域の高さ
- spp:ピクセルあたりのサンプル数、これはズームレベルです
- 解像度:ピクセルあたりのサンプル範囲ごとに取得するサンプル数、パフォーマンスと精度を微調整します。
- スクロール:パフォーマンスのために仮想スクロールが必要になります。これはピクセル単位のスクロール位置です。
- データ:生のオーディオデータ配列、おそらく数百万サンプルの長さ
- drawData:描画に使用される縮小オーディオデータ
ビューポートにあるサンプルのみをオーディオデータから取得し、それらを減らす必要があります。通常、これにより2 *幅のデータセットが生成されます。このデータセットを使用して、画像をレンダリングします。ズームアウトするにはsppを増やし、ズームインするにはそれを減らします。スクロール値を変更するとパンします。
次のコードはO(RN)の複雑さを持っています。ここで、Nは幅、Rは解像度です。最大精度はspp<=解像度です。
コードは次のようになります。これはピーク値を取得します。rmsまたはaverageを実行することもできます。
let reduceAudioPeak = function(data, spp, scroll, width, resolution) {
let drawData = new Array(width);
let startSample = scroll * spp;
let skip = Math.ceil(spp / resolution);
// For each pixel in draw area
for (let i = 0; i < width; i++) {
let min = 0; // minimum value in sample range
let max = 0; // maximum value in sample range
let pixelStartSample = startSample + (i * spp);
// Iterate over the sample range for this pixel (spp)
// and find the min and max values.
for(let j = 0; j < spp; j += skip) {
const index = pixelStartSample + j;
if(index < data.length) {
let val = data[index];
if (val > max) {
max = val;
} else if (val < min) {
min = val;
}
}
}
drawData[i] = [min, max];
}
return drawData;
}
このデータを使用すると、次のように描画できます。線、svgなどを使用できます。
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);
for(let i = 0; i < width; i++) {
// transform data points to pixel height and move to centre
let minPixel = drawData[i][0] * drawHeigth + drawHeight;
let maxPixel = drawData[i][1] * drawHeight + drawHeight;
let pixelHeight = maxPixel - minPixel;
ctx.fillRect(i, minPixel, 1, pixelHeight);
}
}