-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwaveform.js
More file actions
89 lines (83 loc) · 3.52 KB
/
waveform.js
File metadata and controls
89 lines (83 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/**
* Creates a waveform visualizer draw function for use with CanvasManager's render loop.
* @param {AnalyserNode} analyzer - The Web Audio API AnalyserNode.
* @param {number} [_bins=128] - Number of bins (resolution).
* @param {string} [color='#efefef'] - Waveform color.
* @returns {function(canvasData: object): void} - Draw function to be called by CanvasManager's render loop.
*/
export function createWaveform(_analyzer, _bins = 128, color = '#efefef') {
// 10 second buffering
const BUFFER_SECONDS = 5;
const SCALE = 0.9; // 0.9 fills 90% of canvas height
const draw = (manager, {
clear = true,
waveformBuffer,
waveformWriteIndex,
waveformSamplesCollected,
sampleRate = 44100
} = {}) => {
const canvas = manager.el;
const ctx = manager.ctx;
if (clear) {
ctx.clearRect(0, 0, manager.width, manager.height);
}
ctx.save();
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
ctx.fillRect(0, 0, manager.width, manager.height);
ctx.lineWidth = 1.25;
ctx.strokeStyle = color;
ctx.beginPath();
// Defensive checks and debugging
let validSamples = typeof waveformSamplesCollected === 'number' && isFinite(waveformSamplesCollected) && waveformSamplesCollected >= 0;
let validSampleRate = typeof sampleRate === 'number' && isFinite(sampleRate) && sampleRate > 0;
if (!validSamples || !validSampleRate) {
console.warn('[Waveform1] Invalid buffer state:', {
waveformSamplesCollected,
sampleRate,
waveformBuffer,
waveformWriteIndex
});
}
let secondsBuffered = (validSamples && validSampleRate) ? (waveformSamplesCollected / sampleRate) : 0;
let secondsLeft = Math.max(0, BUFFER_SECONDS - secondsBuffered);
// Only draw waveform if enough seconds are buffered
if (!waveformBuffer || secondsBuffered < BUFFER_SECONDS) {
ctx.font = '13px monospace';
ctx.fillStyle = '#efefef';
ctx.textAlign = 'center';
let display;
if (!validSamples || !validSampleRate) {
display = '...';
} else if (secondsLeft >= 1) {
display = `${Math.ceil(secondsLeft)}s`;
} else {
display = `${Math.ceil(secondsLeft * 1000)}ms`;
}
ctx.fillText(`Buffering waveform (${display})`, manager.width / 2, manager.height / 2);
ctx.restore();
return;
}
const bufferSize = waveformBuffer.length;
const start = (waveformWriteIndex + 1) % bufferSize;
ctx.beginPath();
for (let x = 0; x < manager.width; x++) {
const samplesPerPixel = Math.floor(bufferSize / manager.width);
const startSample = (start + x * samplesPerPixel) % bufferSize;
let min = 1, max = -1;
for (let i = 0; i < samplesPerPixel; i++) {
const idx = (startSample + i) % bufferSize;
const v = waveformBuffer[idx];
if (v < min) min = v;
if (v > max) max = v;
}
// Map min/max to canvas coordinates
const y_min = (-min * manager.height * SCALE) / 2 + manager.height / 2;
const y_max = (-max * manager.height * SCALE) / 2 + manager.height / 2;
ctx.moveTo(x, y_min);
ctx.lineTo(x, y_max);
}
ctx.stroke();
ctx.restore();
};
return draw;
};