@modochats/widget 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/.yarn/install-state.gz +0 -0
- package/cdn-dist/README.md +0 -42
- package/cdn-dist/modo-web-component.js +0 -1344
- package/cdn-dist/modo-web-component.min.js +0 -1
- package/cdn-dist/package.json +0 -27
- package/rollup.config.js +0 -18
- package/rollup.dev.config.js +0 -22
- package/scripts/create-umd-bundle.js +0 -213
- package/scripts/terser-minify.js +0 -112
- package/temp/app.dev.js +0 -5101
- package/temp/app.dev.js.map +0 -1
- package/temp/app.js +0 -3570
- package/temp/audio/new-message.mp3 +0 -0
- package/temp/audio/on-hold.mp3 +0 -0
- package/temp/audio-processor.js +0 -261
- package/temp/css/index.css +0 -2283
- package/temp/dev.html +0 -87
- package/temp/index.html +0 -16
|
Binary file
|
package/temp/audio/on-hold.mp3
DELETED
|
Binary file
|
package/temp/audio-processor.js
DELETED
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
class AudioProcessor extends AudioWorkletProcessor {
|
|
2
|
-
constructor() {
|
|
3
|
-
super();
|
|
4
|
-
this.bufferSize = 4096;
|
|
5
|
-
this.buffer = new Float32Array(this.bufferSize);
|
|
6
|
-
this.bufferIndex = 0;
|
|
7
|
-
|
|
8
|
-
this.noiseThreshold = 0.01;
|
|
9
|
-
this.voiceThreshold = 0.08;
|
|
10
|
-
this.silenceFrames = 0;
|
|
11
|
-
this.maxSilenceFrames = 8;
|
|
12
|
-
this.isVoiceActive = false;
|
|
13
|
-
this.isPaused = false;
|
|
14
|
-
|
|
15
|
-
this.noiseProfile = new Float32Array(128).fill(0);
|
|
16
|
-
this.noiseProfileIndex = 0;
|
|
17
|
-
this.noiseProfileReady = false;
|
|
18
|
-
|
|
19
|
-
this.previousSamples = new Float32Array(this.bufferSize).fill(0);
|
|
20
|
-
|
|
21
|
-
this.preRollBuffer = [];
|
|
22
|
-
this.maxPreRollBuffers = 5;
|
|
23
|
-
|
|
24
|
-
this.tailBuffer = [];
|
|
25
|
-
this.maxTailBuffers = 3;
|
|
26
|
-
this.sendingTail = false;
|
|
27
|
-
|
|
28
|
-
this.justResumed = false;
|
|
29
|
-
this.resumedFrameCount = 0;
|
|
30
|
-
this.resumedFrameThreshold = 150;
|
|
31
|
-
|
|
32
|
-
this.port.onmessage = (event) => {
|
|
33
|
-
if (event.data.type === 'set-voice-threshold') {
|
|
34
|
-
this.voiceThreshold = event.data.value;
|
|
35
|
-
} else if (event.data.type === 'set-noise-threshold') {
|
|
36
|
-
this.noiseThreshold = event.data.value;
|
|
37
|
-
} else if (event.data.type === 'pause') {
|
|
38
|
-
this.isPaused = true;
|
|
39
|
-
console.log('AudioProcessor: Input paused');
|
|
40
|
-
} else if (event.data.type === 'resume') {
|
|
41
|
-
this.isPaused = false;
|
|
42
|
-
// Reset VAD state when resuming but KEEP pre-roll buffer
|
|
43
|
-
this.isVoiceActive = false;
|
|
44
|
-
this.silenceFrames = 0;
|
|
45
|
-
// Reset noise profile to quickly adapt to new environment
|
|
46
|
-
this.noiseProfile.fill(0);
|
|
47
|
-
this.noiseProfileIndex = 0;
|
|
48
|
-
this.noiseProfileReady = false;
|
|
49
|
-
// Be extra sensitive for the first ~1 second after resume
|
|
50
|
-
this.justResumed = true;
|
|
51
|
-
this.resumedFrameCount = 0;
|
|
52
|
-
// Don't clear preRollBuffer - it may contain the start of speech
|
|
53
|
-
console.log('AudioProcessor: Input resumed - VAD and noise profile reset, sensitivity boosted');
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
calculateRMS(samples) {
|
|
59
|
-
let sum = 0;
|
|
60
|
-
for (let i = 0; i < samples.length; i++) {
|
|
61
|
-
sum += samples[i] * samples[i];
|
|
62
|
-
}
|
|
63
|
-
return Math.sqrt(sum / samples.length);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
calculateDB(rms) {
|
|
67
|
-
if (rms <= 0 || isNaN(rms)) return -Infinity;
|
|
68
|
-
const db = 20 * Math.log10(rms);
|
|
69
|
-
return isNaN(db) ? -Infinity : db;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
updateNoiseProfile(samples) {
|
|
73
|
-
const rms = this.calculateRMS(samples);
|
|
74
|
-
this.noiseProfile[this.noiseProfileIndex] = rms;
|
|
75
|
-
this.noiseProfileIndex = (this.noiseProfileIndex + 1) % this.noiseProfile.length;
|
|
76
|
-
|
|
77
|
-
if (this.noiseProfileIndex === 0) {
|
|
78
|
-
this.noiseProfileReady = true;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
getNoiseFloor() {
|
|
83
|
-
if (!this.noiseProfileReady) {
|
|
84
|
-
return this.noiseThreshold;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const sorted = Array.from(this.noiseProfile).sort((a, b) => a - b);
|
|
88
|
-
return sorted[Math.floor(sorted.length * 0.25)];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
applySpectralSubtraction(samples) {
|
|
92
|
-
const output = new Float32Array(samples.length);
|
|
93
|
-
const noiseFloor = this.getNoiseFloor();
|
|
94
|
-
|
|
95
|
-
for (let i = 0; i < samples.length; i++) {
|
|
96
|
-
const signal = Math.abs(samples[i]);
|
|
97
|
-
|
|
98
|
-
if (signal < noiseFloor * 2) {
|
|
99
|
-
output[i] = 0;
|
|
100
|
-
} else {
|
|
101
|
-
const cleaned = signal - noiseFloor;
|
|
102
|
-
output[i] = samples[i] >= 0 ? cleaned : -cleaned;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return output;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
applyHighPassFilter(samples) {
|
|
110
|
-
const output = new Float32Array(samples.length);
|
|
111
|
-
const alpha = 0.95;
|
|
112
|
-
|
|
113
|
-
output[0] = samples[0];
|
|
114
|
-
for (let i = 1; i < samples.length; i++) {
|
|
115
|
-
output[i] = alpha * (output[i-1] + samples[i] - this.previousSamples[i]);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
this.previousSamples.set(samples);
|
|
119
|
-
return output;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
applyNoiseGate(samples, rms) {
|
|
123
|
-
const noiseFloor = this.getNoiseFloor();
|
|
124
|
-
let adaptiveThreshold = Math.max(this.noiseThreshold, noiseFloor * 1.8);
|
|
125
|
-
|
|
126
|
-
if (this.justResumed) {
|
|
127
|
-
adaptiveThreshold = adaptiveThreshold * 0.3;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (rms < adaptiveThreshold) {
|
|
131
|
-
return new Float32Array(samples.length);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return samples;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
detectVoice(rms) {
|
|
138
|
-
const noiseFloor = this.getNoiseFloor();
|
|
139
|
-
|
|
140
|
-
let adaptiveVoiceThreshold;
|
|
141
|
-
if (this.justResumed) {
|
|
142
|
-
this.resumedFrameCount++;
|
|
143
|
-
|
|
144
|
-
const boostProgress = Math.min(this.resumedFrameCount / this.resumedFrameThreshold, 1.0);
|
|
145
|
-
const boostMultiplier = 0.2 + (0.8 * boostProgress);
|
|
146
|
-
|
|
147
|
-
adaptiveVoiceThreshold = this.voiceThreshold * boostMultiplier;
|
|
148
|
-
|
|
149
|
-
if (this.resumedFrameCount % 20 === 0) {
|
|
150
|
-
console.log(`AudioProcessor: Boosted threshold: ${adaptiveVoiceThreshold.toFixed(4)} (${(boostMultiplier * 100).toFixed(0)}% of normal, RMS: ${rms.toFixed(4)})`);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (this.resumedFrameCount > this.resumedFrameThreshold) {
|
|
154
|
-
this.justResumed = false;
|
|
155
|
-
console.log('AudioProcessor: Sensitivity boost ended, returning to adaptive threshold');
|
|
156
|
-
}
|
|
157
|
-
} else {
|
|
158
|
-
const multiplier = 1.8;
|
|
159
|
-
adaptiveVoiceThreshold = Math.max(this.voiceThreshold, noiseFloor * multiplier);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const wasActive = this.isVoiceActive;
|
|
163
|
-
|
|
164
|
-
if (rms > adaptiveVoiceThreshold) {
|
|
165
|
-
if (!this.isVoiceActive) {
|
|
166
|
-
this.isVoiceActive = true;
|
|
167
|
-
if (this.justResumed) {
|
|
168
|
-
console.log('AudioProcessor: Voice detected with boost!');
|
|
169
|
-
}
|
|
170
|
-
return { justActivated: true, isActive: true };
|
|
171
|
-
}
|
|
172
|
-
this.isVoiceActive = true;
|
|
173
|
-
this.silenceFrames = 0;
|
|
174
|
-
} else if (this.isVoiceActive) {
|
|
175
|
-
this.silenceFrames++;
|
|
176
|
-
if (this.silenceFrames > this.maxSilenceFrames) {
|
|
177
|
-
this.isVoiceActive = false;
|
|
178
|
-
this.sendingTail = true;
|
|
179
|
-
this.preRollBuffer = [];
|
|
180
|
-
return { justActivated: false, isActive: false, justDeactivated: true };
|
|
181
|
-
}
|
|
182
|
-
} else {
|
|
183
|
-
this.updateNoiseProfile(this.buffer);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return { justActivated: false, isActive: this.isVoiceActive, justDeactivated: false };
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
process(inputs, outputs, parameters) {
|
|
190
|
-
const input = inputs[0];
|
|
191
|
-
if (input.length > 0) {
|
|
192
|
-
const inputChannel = input[0];
|
|
193
|
-
|
|
194
|
-
for (let i = 0; i < inputChannel.length; i++) {
|
|
195
|
-
this.buffer[this.bufferIndex] = inputChannel[i];
|
|
196
|
-
this.bufferIndex++;
|
|
197
|
-
|
|
198
|
-
if (this.bufferIndex >= this.bufferSize) {
|
|
199
|
-
const rms = this.calculateRMS(this.buffer);
|
|
200
|
-
const db = this.calculateDB(rms);
|
|
201
|
-
const voiceStatus = this.detectVoice(rms);
|
|
202
|
-
|
|
203
|
-
this.port.postMessage({
|
|
204
|
-
type: 'voice-level',
|
|
205
|
-
rms: rms,
|
|
206
|
-
db: db,
|
|
207
|
-
isActive: voiceStatus.isActive,
|
|
208
|
-
noiseFloor: this.getNoiseFloor(),
|
|
209
|
-
isPaused: this.isPaused
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
if (!this.isPaused) {
|
|
213
|
-
let processedBuffer = this.buffer;
|
|
214
|
-
|
|
215
|
-
const int16Buffer = new Int16Array(this.bufferSize);
|
|
216
|
-
for (let j = 0; j < this.bufferSize; j++) {
|
|
217
|
-
const s = Math.max(-1, Math.min(1, processedBuffer[j]));
|
|
218
|
-
int16Buffer[j] = s < 0 ? s * 0x8000 : s * 0x7FFF;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if (voiceStatus.justActivated && this.preRollBuffer.length > 0) {
|
|
222
|
-
for (const preRollBuffer of this.preRollBuffer) {
|
|
223
|
-
this.port.postMessage(preRollBuffer);
|
|
224
|
-
}
|
|
225
|
-
this.preRollBuffer = [];
|
|
226
|
-
this.tailBuffer = [];
|
|
227
|
-
this.sendingTail = false;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (voiceStatus.isActive) {
|
|
231
|
-
this.port.postMessage(int16Buffer.buffer);
|
|
232
|
-
this.tailBuffer = [];
|
|
233
|
-
this.sendingTail = false;
|
|
234
|
-
} else if (this.sendingTail) {
|
|
235
|
-
this.tailBuffer.push(int16Buffer.buffer);
|
|
236
|
-
if (this.tailBuffer.length >= this.maxTailBuffers) {
|
|
237
|
-
for (const tailBuffer of this.tailBuffer) {
|
|
238
|
-
this.port.postMessage(tailBuffer);
|
|
239
|
-
}
|
|
240
|
-
this.port.postMessage({ type: 'voice-ended' });
|
|
241
|
-
this.tailBuffer = [];
|
|
242
|
-
this.sendingTail = false;
|
|
243
|
-
}
|
|
244
|
-
} else {
|
|
245
|
-
this.preRollBuffer.push(int16Buffer.buffer);
|
|
246
|
-
if (this.preRollBuffer.length > this.maxPreRollBuffers) {
|
|
247
|
-
this.preRollBuffer.shift();
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
this.bufferIndex = 0;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return true;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
registerProcessor('audio-processor', AudioProcessor);
|