-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaudio.js
More file actions
190 lines (163 loc) · 5.64 KB
/
Copy pathaudio.js
File metadata and controls
190 lines (163 loc) · 5.64 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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
export async function start(update) {
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
});
const audioContext = new AudioContext();
const source = audioContext.createMediaStreamSource(stream);
const analyser = audioContext.createAnalyser();
source.connect(analyser);
analyser.fftSize = 2048;
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
const volumeHistory = [];
const frequencyHistory = [];
const avgVolumeHistory = [];
const avgFrequencyHistory = [];
const maxHistoryLength = (0.5 * 1000) / (1000 / 60);
const debugCanvas = document.getElementById("debugCanvas");
const debugCtx = debugCanvas.getContext("2d");
const spectrogramCanvas = document.getElementById("spectrogramCanvas");
const spectrogramCtx = spectrogramCanvas.getContext("2d");
// Define static ranges
const maxVolume = 255;
const maxFrequency = 255;
function drawDebugInfo() {
debugCtx.clearRect(0, 0, debugCanvas.width, debugCanvas.height);
// Draw axes
debugCtx.strokeStyle = "black";
debugCtx.lineWidth = 1;
debugCtx.beginPath();
debugCtx.moveTo(30, 10);
debugCtx.lineTo(30, debugCanvas.height - 30);
debugCtx.lineTo(debugCanvas.width - 10, debugCanvas.height - 30);
debugCtx.stroke();
// Draw average volume history with static range
debugCtx.strokeStyle = "blue";
debugCtx.lineWidth = 2;
debugCtx.beginPath();
for (let i = 0; i < avgVolumeHistory.length; i++) {
const x = 30 + (i / avgVolumeHistory.length) * (debugCanvas.width - 40);
const y =
debugCanvas.height -
30 -
(avgVolumeHistory[i] / maxVolume) * (debugCanvas.height - 40);
if (i === 0) {
debugCtx.moveTo(x, y);
} else {
debugCtx.lineTo(x, y);
}
}
debugCtx.stroke();
// Draw average frequency history with static range
debugCtx.strokeStyle = "red";
debugCtx.lineWidth = 2;
debugCtx.beginPath();
for (let i = 0; i < avgFrequencyHistory.length; i++) {
const x =
30 + (i / avgFrequencyHistory.length) * (debugCanvas.width - 40);
const y =
debugCanvas.height -
30 -
(avgFrequencyHistory[i] / maxFrequency) * (debugCanvas.height - 40);
if (i === 0) {
debugCtx.moveTo(x, y);
} else {
debugCtx.lineTo(x, y);
}
}
debugCtx.stroke();
// Add labels
debugCtx.fillStyle = "black";
debugCtx.font = "12px Arial";
debugCtx.fillText("Avg Volume", 35, 20);
debugCtx.fillText("Avg Frequency", 35, 40);
debugCtx.fillText("Time", debugCanvas.width - 40, debugCanvas.height - 10);
}
function drawBarSpectrogram() {
analyser.getByteFrequencyData(dataArray);
spectrogramCtx.clearRect(
0,
0,
spectrogramCanvas.width,
spectrogramCanvas.height
);
const barWidth = spectrogramCanvas.width / bufferLength;
for (let i = 0; i < bufferLength; i++) {
const barHeight = (dataArray[i] / 255) * spectrogramCanvas.height;
// Focus on typical human speech frequency range
if ((i * audioContext.sampleRate) / analyser.fftSize < maxFrequency) {
spectrogramCtx.fillStyle = `rgb(${dataArray[i]}, 50, 50)`;
spectrogramCtx.fillRect(
i * barWidth,
spectrogramCanvas.height - barHeight,
barWidth,
barHeight
);
}
}
}
function analyzeAudio() {
analyser.getByteTimeDomainData(dataArray);
// Calculate volume
let volume = 0;
for (let i = 0; i < bufferLength; i++) {
volume += Math.abs(dataArray[i] - 128);
}
volume = volume / bufferLength;
// Store the current volume
volumeHistory.push(volume);
// Only process frequency if volume is greater than 1
if (volume > 1) {
// Find the frequency with the highest amplitude in the human speech range
let maxFrequencyValue = 0;
let maxIndex = 0;
analyser.getByteFrequencyData(dataArray);
for (let i = 0; i < bufferLength; i++) {
const frequency = (i * audioContext.sampleRate) / analyser.fftSize;
if (
frequency > 80 &&
frequency < maxFrequency &&
dataArray[i] > maxFrequencyValue
) {
maxFrequencyValue = dataArray[i];
maxIndex = i;
}
}
const frequency = (maxIndex * audioContext.sampleRate) / analyser.fftSize;
frequencyHistory.push(frequency);
} else {
frequencyHistory.push(0); // Push a placeholder value if volume is not greater than 1
}
// Keep only the last 2 seconds of data
if (volumeHistory.length > maxHistoryLength) {
volumeHistory.shift();
frequencyHistory.shift();
}
// Calculate the average volume and frequency over the last 2 seconds
const avgVolume =
volumeHistory.reduce((a, b) => a + b, 0) / volumeHistory.length;
const validFrequencies = frequencyHistory.filter((f) => f > 0);
const avgFrequency =
validFrequencies.length > 0
? validFrequencies.reduce((a, b) => a + b, 0) / validFrequencies.length
: 0;
// Store average values for historical debug output
avgVolumeHistory.push(avgVolume);
avgFrequencyHistory.push(avgFrequency);
// Limit the history length for averages
if (avgVolumeHistory.length > 100) {
avgVolumeHistory.shift();
avgFrequencyHistory.shift();
}
document.getElementById(
"output"
).innerText = `Average Volume: ${avgVolume.toFixed(
2
)}\nAverage Frequency: ${avgFrequency.toFixed(2)} Hz`;
update(avgVolume, avgFrequency);
drawDebugInfo();
drawBarSpectrogram();
requestAnimationFrame(analyzeAudio);
}
analyzeAudio();
}