ugly-app 0.1.282 → 0.1.283
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/dist/cli/version.d.ts +1 -1
- package/dist/cli/version.js +1 -1
- package/dist/client/audio/AudioRecorder.d.ts.map +1 -1
- package/dist/client/audio/AudioRecorder.js +25 -0
- package/dist/client/audio/AudioRecorder.js.map +1 -1
- package/dist/client/audio/useSTT.d.ts.map +1 -1
- package/dist/client/audio/useSTT.js +26 -4
- package/dist/client/audio/useSTT.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/version.ts +1 -1
- package/src/client/audio/AudioRecorder.ts +28 -0
- package/src/client/audio/useSTT.ts +28 -4
- package/templates/client/pages/AudioTestPage.tsx +7 -2
package/dist/cli/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.283";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/cli/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioRecorder.d.ts","sourceRoot":"","sources":["../../../src/client/audio/AudioRecorder.ts"],"names":[],"mappings":"AA8BA,qBAAa,aAAa;IACxB,OAAO,CAAC,GAAG,CAA6B;IACxC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAA2C;IAE1D,IAAI,SAAS,IAAI,OAAO,CAEvB;IAEK,KAAK,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"AudioRecorder.d.ts","sourceRoot":"","sources":["../../../src/client/audio/AudioRecorder.ts"],"names":[],"mappings":"AA8BA,qBAAa,aAAa;IACxB,OAAO,CAAC,GAAG,CAA6B;IACxC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAA2C;IAE1D,IAAI,SAAS,IAAI,OAAO,CAEvB;IAEK,KAAK,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IA2DlE,IAAI,IAAI,IAAI;CAYb"}
|
|
@@ -40,6 +40,7 @@ export class AudioRecorder {
|
|
|
40
40
|
if (this._listening)
|
|
41
41
|
return;
|
|
42
42
|
this.onChunk = onChunk;
|
|
43
|
+
console.log('[AudioRecorder] requesting mic access…');
|
|
43
44
|
this.stream = await navigator.mediaDevices.getUserMedia({
|
|
44
45
|
audio: {
|
|
45
46
|
sampleRate: TARGET_SAMPLE_RATE,
|
|
@@ -48,13 +49,35 @@ export class AudioRecorder {
|
|
|
48
49
|
noiseSuppression: true,
|
|
49
50
|
},
|
|
50
51
|
});
|
|
52
|
+
const track = this.stream.getAudioTracks()[0];
|
|
53
|
+
if (track) {
|
|
54
|
+
const settings = track.getSettings();
|
|
55
|
+
console.log(`[AudioRecorder] mic acquired: ${track.label} (${settings.sampleRate ?? '?'}Hz, ${settings.channelCount ?? '?'}ch)`);
|
|
56
|
+
}
|
|
51
57
|
this.ctx = new AudioContext({ sampleRate: TARGET_SAMPLE_RATE });
|
|
58
|
+
console.log(`[AudioRecorder] AudioContext state=${this.ctx.state} sampleRate=${this.ctx.sampleRate}`);
|
|
52
59
|
const source = this.ctx.createMediaStreamSource(this.stream);
|
|
53
60
|
await this.ctx.audioWorklet.addModule(WORKLET_URL);
|
|
61
|
+
console.log('[AudioRecorder] worklet loaded');
|
|
54
62
|
this.worklet = new AudioWorkletNode(this.ctx, 'audio-recorder');
|
|
63
|
+
let chunkCount = 0;
|
|
64
|
+
let silentChunks = 0;
|
|
55
65
|
this.worklet.port.onmessage = (event) => {
|
|
56
66
|
if (event.data?.type === 'chunk') {
|
|
57
67
|
const floatData = event.data.samples;
|
|
68
|
+
chunkCount++;
|
|
69
|
+
// Check for silence (all samples near zero)
|
|
70
|
+
let maxAbs = 0;
|
|
71
|
+
for (let i = 0; i < floatData.length; i += 64) {
|
|
72
|
+
const abs = Math.abs(floatData[i] ?? 0);
|
|
73
|
+
if (abs > maxAbs)
|
|
74
|
+
maxAbs = abs;
|
|
75
|
+
}
|
|
76
|
+
if (maxAbs < 0.001)
|
|
77
|
+
silentChunks++;
|
|
78
|
+
if (chunkCount <= 3 || chunkCount % 100 === 0) {
|
|
79
|
+
console.log(`[AudioRecorder] chunk #${chunkCount} maxAbs=${maxAbs.toFixed(4)} silentChunks=${silentChunks}/${chunkCount}`);
|
|
80
|
+
}
|
|
58
81
|
const int16 = float32ToInt16(floatData);
|
|
59
82
|
const base64 = arrayBufferToBase64(int16.buffer);
|
|
60
83
|
this.onChunk?.(base64);
|
|
@@ -63,10 +86,12 @@ export class AudioRecorder {
|
|
|
63
86
|
source.connect(this.worklet);
|
|
64
87
|
this.worklet.connect(this.ctx.destination);
|
|
65
88
|
this._listening = true;
|
|
89
|
+
console.log('[AudioRecorder] recording started');
|
|
66
90
|
}
|
|
67
91
|
stop() {
|
|
68
92
|
if (!this._listening)
|
|
69
93
|
return;
|
|
94
|
+
console.log('[AudioRecorder] stopping');
|
|
70
95
|
this.worklet?.disconnect();
|
|
71
96
|
this.stream?.getTracks().forEach((t) => t.stop());
|
|
72
97
|
this.ctx?.close().catch(() => { });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioRecorder.js","sourceRoot":"","sources":["../../../src/client/audio/AudioRecorder.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;CAwBpB,CAAC;AACF,MAAM,YAAY,GAAG,IAAI,IAAI,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE,CAAC,CAAC;AAClF,MAAM,WAAW,GAAG,GAAG,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;AACtD,MAAM,kBAAkB,GAAG,KAAK,CAAC;AAEjC,MAAM,OAAO,aAAa;IAChB,GAAG,GAAwB,IAAI,CAAC;IAChC,MAAM,GAAuB,IAAI,CAAC;IAClC,OAAO,GAA4B,IAAI,CAAC;IACxC,UAAU,GAAG,KAAK,CAAC;IACnB,OAAO,GAAsC,IAAI,CAAC;IAE1D,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,OAAsC;QAChD,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC5B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QAEvB,IAAI,CAAC,MAAM,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC;YACtD,KAAK,EAAE;gBACL,UAAU,EAAE,kBAAkB;gBAC9B,YAAY,EAAE,CAAC;gBACf,gBAAgB,EAAE,IAAI;gBACtB,gBAAgB,EAAE,IAAI;aACvB;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,GAAG,IAAI,YAAY,CAAC,EAAE,UAAU,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAChE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,uBAAuB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAE7D,MAAM,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACnD,IAAI,CAAC,OAAO,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"AudioRecorder.js","sourceRoot":"","sources":["../../../src/client/audio/AudioRecorder.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSTT.d.ts","sourceRoot":"","sources":["../../../src/client/audio/useSTT.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID,wBAAgB,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,UAAe;;;;;;;
|
|
1
|
+
{"version":3,"file":"useSTT.d.ts","sourceRoot":"","sources":["../../../src/client/audio/useSTT.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID,wBAAgB,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,UAAe;;;;;;;EA+HrE"}
|
|
@@ -28,20 +28,27 @@ export function useSTT(socket, options = {}) {
|
|
|
28
28
|
setIsFinal(false);
|
|
29
29
|
setError(null);
|
|
30
30
|
setListening(true);
|
|
31
|
+
const mode = options.mode ?? 'auto';
|
|
32
|
+
console.log(`[STT] start streamId=${streamId} mode=${mode} lang=${options.lang ?? 'auto'}`);
|
|
31
33
|
await socket.acquire();
|
|
34
|
+
console.log('[STT] socket acquired');
|
|
32
35
|
cleanupListeners();
|
|
36
|
+
let chunkCount = 0;
|
|
33
37
|
unsubsRef.current.push(socket.on('stt:ready', (data) => {
|
|
34
38
|
if (data.streamId !== streamId)
|
|
35
39
|
return;
|
|
40
|
+
console.log(`[STT] ready streamId=${streamId}`);
|
|
36
41
|
readyRef.current = true;
|
|
37
42
|
}), socket.on('stt:transcript', (data) => {
|
|
38
43
|
if (data.streamId !== streamId)
|
|
39
44
|
return;
|
|
45
|
+
console.log(`[STT] transcript isFinal=${data.isFinal} text="${data.text}" (after ${chunkCount} chunks)`);
|
|
40
46
|
setTranscript(data.text);
|
|
41
47
|
setIsFinal(data.isFinal);
|
|
42
48
|
}), socket.on('stt:error', (data) => {
|
|
43
49
|
if (data.streamId !== streamId)
|
|
44
50
|
return;
|
|
51
|
+
console.error(`[STT] error streamId=${streamId}: ${data.message}`);
|
|
45
52
|
setError(data.message);
|
|
46
53
|
setListening(false);
|
|
47
54
|
cleanupListeners();
|
|
@@ -49,34 +56,49 @@ export function useSTT(socket, options = {}) {
|
|
|
49
56
|
}), socket.on('stt:stopped', (data) => {
|
|
50
57
|
if (data.streamId !== streamId)
|
|
51
58
|
return;
|
|
59
|
+
console.log(`[STT] stopped streamId=${streamId} (sent ${chunkCount} audio chunks)`);
|
|
52
60
|
setListening(false);
|
|
53
61
|
cleanupListeners();
|
|
54
62
|
socket.release();
|
|
55
63
|
}));
|
|
56
64
|
socket.send('stt:start', {
|
|
57
65
|
streamId,
|
|
58
|
-
mode
|
|
66
|
+
mode,
|
|
59
67
|
lang: options.lang,
|
|
60
68
|
});
|
|
61
69
|
// Wait for stt:ready (timeout after 5s)
|
|
70
|
+
const readyStart = Date.now();
|
|
62
71
|
await new Promise((resolve) => {
|
|
63
|
-
const startTime = Date.now();
|
|
64
72
|
const poll = setInterval(() => {
|
|
65
|
-
if (readyRef.current || Date.now() -
|
|
73
|
+
if (readyRef.current || Date.now() - readyStart > 5000) {
|
|
66
74
|
clearInterval(poll);
|
|
67
75
|
resolve();
|
|
68
76
|
}
|
|
69
77
|
}, 50);
|
|
70
78
|
});
|
|
71
|
-
if (
|
|
79
|
+
if (!readyRef.current) {
|
|
80
|
+
console.warn(`[STT] ready timeout after ${Date.now() - readyStart}ms — proceeding anyway`);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
console.log(`[STT] ready in ${Date.now() - readyStart}ms`);
|
|
84
|
+
}
|
|
85
|
+
if (cancelledRef.current) {
|
|
86
|
+
console.log('[STT] cancelled before recording started');
|
|
72
87
|
return;
|
|
88
|
+
}
|
|
73
89
|
if (!recorderRef.current)
|
|
74
90
|
recorderRef.current = new AudioRecorder();
|
|
75
91
|
await recorderRef.current.start((audio) => {
|
|
92
|
+
chunkCount++;
|
|
93
|
+
if (chunkCount <= 3 || chunkCount % 50 === 0) {
|
|
94
|
+
console.log(`[STT] audio chunk #${chunkCount} (${audio.length} b64 chars)`);
|
|
95
|
+
}
|
|
76
96
|
socket.send('stt:audio', { streamId, audio });
|
|
77
97
|
});
|
|
98
|
+
console.log('[STT] recording started');
|
|
78
99
|
}, [socket, listening, options.mode, options.lang]);
|
|
79
100
|
const stop = useCallback(() => {
|
|
101
|
+
console.log(`[STT] stop streamId=${streamIdRef.current}`);
|
|
80
102
|
cancelledRef.current = true;
|
|
81
103
|
recorderRef.current?.stop();
|
|
82
104
|
if (streamIdRef.current) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSTT.js","sourceRoot":"","sources":["../../../src/client/audio/useSTT.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAOnD,IAAI,UAAU,GAAG,CAAC,CAAC;AAEnB,MAAM,UAAU,MAAM,CAAC,MAAqB,EAAE,UAAsB,EAAE;IACpE,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACjD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAExD,MAAM,WAAW,GAAG,MAAM,CAAuB,IAAI,CAAC,CAAC;IACvD,MAAM,WAAW,GAAG,MAAM,CAAgB,IAAI,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,SAAS,GAAG,MAAM,CAAiB,EAAE,CAAC,CAAC;IAE7C,SAAS,gBAAgB;QACvB,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,OAAO;YAAE,KAAK,EAAE,CAAC;QAC/C,SAAS,CAAC,OAAO,GAAG,EAAE,CAAC;IACzB,CAAC;IAED,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACnC,IAAI,SAAS;YAAE,OAAO;QACtB,MAAM,QAAQ,GAAG,OAAO,EAAE,UAAU,EAAE,CAAC;QACvC,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;QAC/B,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAC;QACzB,YAAY,CAAC,OAAO,GAAG,KAAK,CAAC;QAC7B,aAAa,CAAC,EAAE,CAAC,CAAC;QAClB,UAAU,CAAC,KAAK,CAAC,CAAC;QAClB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,YAAY,CAAC,IAAI,CAAC,CAAC;QAEnB,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"useSTT.js","sourceRoot":"","sources":["../../../src/client/audio/useSTT.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/cli/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by prebuild — do not edit manually
|
|
2
|
-
export const CLI_VERSION = "0.1.
|
|
2
|
+
export const CLI_VERSION = "0.1.283";
|
|
@@ -43,6 +43,7 @@ export class AudioRecorder {
|
|
|
43
43
|
if (this._listening) return;
|
|
44
44
|
this.onChunk = onChunk;
|
|
45
45
|
|
|
46
|
+
console.log('[AudioRecorder] requesting mic access…');
|
|
46
47
|
this.stream = await navigator.mediaDevices.getUserMedia({
|
|
47
48
|
audio: {
|
|
48
49
|
sampleRate: TARGET_SAMPLE_RATE,
|
|
@@ -52,14 +53,39 @@ export class AudioRecorder {
|
|
|
52
53
|
},
|
|
53
54
|
});
|
|
54
55
|
|
|
56
|
+
const track = this.stream.getAudioTracks()[0];
|
|
57
|
+
if (track) {
|
|
58
|
+
const settings = track.getSettings();
|
|
59
|
+
console.log(`[AudioRecorder] mic acquired: ${track.label} (${settings.sampleRate ?? '?'}Hz, ${settings.channelCount ?? '?'}ch)`);
|
|
60
|
+
}
|
|
61
|
+
|
|
55
62
|
this.ctx = new AudioContext({ sampleRate: TARGET_SAMPLE_RATE });
|
|
63
|
+
console.log(`[AudioRecorder] AudioContext state=${this.ctx.state} sampleRate=${this.ctx.sampleRate}`);
|
|
56
64
|
const source = this.ctx.createMediaStreamSource(this.stream);
|
|
57
65
|
|
|
58
66
|
await this.ctx.audioWorklet.addModule(WORKLET_URL);
|
|
67
|
+
console.log('[AudioRecorder] worklet loaded');
|
|
59
68
|
this.worklet = new AudioWorkletNode(this.ctx, 'audio-recorder');
|
|
69
|
+
|
|
70
|
+
let chunkCount = 0;
|
|
71
|
+
let silentChunks = 0;
|
|
60
72
|
this.worklet.port.onmessage = (event: MessageEvent) => {
|
|
61
73
|
if (event.data?.type === 'chunk') {
|
|
62
74
|
const floatData = event.data.samples as Float32Array;
|
|
75
|
+
chunkCount++;
|
|
76
|
+
|
|
77
|
+
// Check for silence (all samples near zero)
|
|
78
|
+
let maxAbs = 0;
|
|
79
|
+
for (let i = 0; i < floatData.length; i += 64) {
|
|
80
|
+
const abs = Math.abs(floatData[i] ?? 0);
|
|
81
|
+
if (abs > maxAbs) maxAbs = abs;
|
|
82
|
+
}
|
|
83
|
+
if (maxAbs < 0.001) silentChunks++;
|
|
84
|
+
|
|
85
|
+
if (chunkCount <= 3 || chunkCount % 100 === 0) {
|
|
86
|
+
console.log(`[AudioRecorder] chunk #${chunkCount} maxAbs=${maxAbs.toFixed(4)} silentChunks=${silentChunks}/${chunkCount}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
63
89
|
const int16 = float32ToInt16(floatData);
|
|
64
90
|
const base64 = arrayBufferToBase64(int16.buffer as ArrayBuffer);
|
|
65
91
|
this.onChunk?.(base64);
|
|
@@ -69,10 +95,12 @@ export class AudioRecorder {
|
|
|
69
95
|
source.connect(this.worklet);
|
|
70
96
|
this.worklet.connect(this.ctx.destination);
|
|
71
97
|
this._listening = true;
|
|
98
|
+
console.log('[AudioRecorder] recording started');
|
|
72
99
|
}
|
|
73
100
|
|
|
74
101
|
stop(): void {
|
|
75
102
|
if (!this._listening) return;
|
|
103
|
+
console.log('[AudioRecorder] stopping');
|
|
76
104
|
this.worklet?.disconnect();
|
|
77
105
|
this.stream?.getTracks().forEach((t) => t.stop());
|
|
78
106
|
this.ctx?.close().catch(() => {});
|
|
@@ -38,21 +38,29 @@ export function useSTT(socket: UglyBotSocket, options: STTOptions = {}) {
|
|
|
38
38
|
setError(null);
|
|
39
39
|
setListening(true);
|
|
40
40
|
|
|
41
|
+
const mode = options.mode ?? 'auto';
|
|
42
|
+
console.log(`[STT] start streamId=${streamId} mode=${mode} lang=${options.lang ?? 'auto'}`);
|
|
43
|
+
|
|
41
44
|
await socket.acquire();
|
|
45
|
+
console.log('[STT] socket acquired');
|
|
42
46
|
|
|
43
47
|
cleanupListeners();
|
|
48
|
+
let chunkCount = 0;
|
|
44
49
|
unsubsRef.current.push(
|
|
45
50
|
socket.on('stt:ready', (data) => {
|
|
46
51
|
if (data.streamId !== streamId) return;
|
|
52
|
+
console.log(`[STT] ready streamId=${streamId}`);
|
|
47
53
|
readyRef.current = true;
|
|
48
54
|
}),
|
|
49
55
|
socket.on('stt:transcript', (data) => {
|
|
50
56
|
if (data.streamId !== streamId) return;
|
|
57
|
+
console.log(`[STT] transcript isFinal=${data.isFinal} text="${data.text}" (after ${chunkCount} chunks)`);
|
|
51
58
|
setTranscript(data.text);
|
|
52
59
|
setIsFinal(data.isFinal);
|
|
53
60
|
}),
|
|
54
61
|
socket.on('stt:error', (data) => {
|
|
55
62
|
if (data.streamId !== streamId) return;
|
|
63
|
+
console.error(`[STT] error streamId=${streamId}: ${data.message}`);
|
|
56
64
|
setError(data.message);
|
|
57
65
|
setListening(false);
|
|
58
66
|
cleanupListeners();
|
|
@@ -60,6 +68,7 @@ export function useSTT(socket: UglyBotSocket, options: STTOptions = {}) {
|
|
|
60
68
|
}),
|
|
61
69
|
socket.on('stt:stopped', (data) => {
|
|
62
70
|
if (data.streamId !== streamId) return;
|
|
71
|
+
console.log(`[STT] stopped streamId=${streamId} (sent ${chunkCount} audio chunks)`);
|
|
63
72
|
setListening(false);
|
|
64
73
|
cleanupListeners();
|
|
65
74
|
socket.release();
|
|
@@ -68,30 +77,45 @@ export function useSTT(socket: UglyBotSocket, options: STTOptions = {}) {
|
|
|
68
77
|
|
|
69
78
|
socket.send('stt:start', {
|
|
70
79
|
streamId,
|
|
71
|
-
mode
|
|
80
|
+
mode,
|
|
72
81
|
lang: options.lang,
|
|
73
82
|
});
|
|
74
83
|
|
|
75
84
|
// Wait for stt:ready (timeout after 5s)
|
|
85
|
+
const readyStart = Date.now();
|
|
76
86
|
await new Promise<void>((resolve) => {
|
|
77
|
-
const startTime = Date.now();
|
|
78
87
|
const poll = setInterval(() => {
|
|
79
|
-
if (readyRef.current || Date.now() -
|
|
88
|
+
if (readyRef.current || Date.now() - readyStart > 5000) {
|
|
80
89
|
clearInterval(poll);
|
|
81
90
|
resolve();
|
|
82
91
|
}
|
|
83
92
|
}, 50);
|
|
84
93
|
});
|
|
85
94
|
|
|
86
|
-
if (
|
|
95
|
+
if (!readyRef.current) {
|
|
96
|
+
console.warn(`[STT] ready timeout after ${Date.now() - readyStart}ms — proceeding anyway`);
|
|
97
|
+
} else {
|
|
98
|
+
console.log(`[STT] ready in ${Date.now() - readyStart}ms`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (cancelledRef.current) {
|
|
102
|
+
console.log('[STT] cancelled before recording started');
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
87
105
|
|
|
88
106
|
if (!recorderRef.current) recorderRef.current = new AudioRecorder();
|
|
89
107
|
await recorderRef.current.start((audio) => {
|
|
108
|
+
chunkCount++;
|
|
109
|
+
if (chunkCount <= 3 || chunkCount % 50 === 0) {
|
|
110
|
+
console.log(`[STT] audio chunk #${chunkCount} (${audio.length} b64 chars)`);
|
|
111
|
+
}
|
|
90
112
|
socket.send('stt:audio', { streamId, audio });
|
|
91
113
|
});
|
|
114
|
+
console.log('[STT] recording started');
|
|
92
115
|
}, [socket, listening, options.mode, options.lang]);
|
|
93
116
|
|
|
94
117
|
const stop = useCallback(() => {
|
|
118
|
+
console.log(`[STT] stop streamId=${streamIdRef.current}`);
|
|
95
119
|
cancelledRef.current = true;
|
|
96
120
|
recorderRef.current?.stop();
|
|
97
121
|
if (streamIdRef.current) {
|
|
@@ -203,6 +203,7 @@ function useTTSAnalyzer() {
|
|
|
203
203
|
const ctxRef = useRef<AudioContext | null>(null);
|
|
204
204
|
const analyzerRef = useRef<AnalyserNode | null>(null);
|
|
205
205
|
const gainRef = useRef<GainNode | null>(null);
|
|
206
|
+
const nextStartRef = useRef(0);
|
|
206
207
|
const [analyzer, setAnalyzer] = useState<AnalyserNode | null>(null);
|
|
207
208
|
|
|
208
209
|
const init = useCallback(() => {
|
|
@@ -216,6 +217,7 @@ function useTTSAnalyzer() {
|
|
|
216
217
|
ctxRef.current = ctx;
|
|
217
218
|
analyzerRef.current = node;
|
|
218
219
|
gainRef.current = gain;
|
|
220
|
+
nextStartRef.current = 0;
|
|
219
221
|
setAnalyzer(node);
|
|
220
222
|
}, []);
|
|
221
223
|
|
|
@@ -232,7 +234,10 @@ function useTTSAnalyzer() {
|
|
|
232
234
|
const source = ctx.createBufferSource();
|
|
233
235
|
source.buffer = buffer;
|
|
234
236
|
source.connect(gain);
|
|
235
|
-
|
|
237
|
+
const now = ctx.currentTime;
|
|
238
|
+
if (nextStartRef.current < now) nextStartRef.current = now;
|
|
239
|
+
source.start(nextStartRef.current);
|
|
240
|
+
nextStartRef.current += buffer.duration;
|
|
236
241
|
}, []);
|
|
237
242
|
|
|
238
243
|
const close = useCallback(() => {
|
|
@@ -484,7 +489,7 @@ function STTSection({ socket }: { socket: UglyBotSocket }) {
|
|
|
484
489
|
let ttsCounter = 0;
|
|
485
490
|
|
|
486
491
|
function TTSSection({ socket }: { socket: UglyBotSocket }) {
|
|
487
|
-
const [text, setText] = useState('');
|
|
492
|
+
const [text, setText] = useState('The quick brown fox jumps over the lazy dog. Hello, this is a text to speech test.');
|
|
488
493
|
const [voice, setVoice] = useState('');
|
|
489
494
|
const [vizType, setVizType] = useState<VizType>('wave');
|
|
490
495
|
const [playing, setPlaying] = useState(false);
|