@charivo/stt-core 0.0.1
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/LICENSE +21 -0
- package/README.md +463 -0
- package/dist/index.d.mts +70 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +154 -0
- package/dist/index.mjs +125 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Zeikar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
# @charivo/stt-core
|
|
2
|
+
|
|
3
|
+
Core STT (Speech-to-Text) functionality with transcription coordination, event emission, and shared utilities for Charivo.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎤 **Transcription Coordination** - Manages STT transcribers with unified API
|
|
8
|
+
- 📡 **Event Bus Integration** - Emit audio events for recording lifecycle
|
|
9
|
+
- 🛠️ **MediaRecorder Helper** - Shared audio recording utility for transcribers
|
|
10
|
+
- 🔌 **Transcriber Agnostic** - Works with any STT transcriber (Web, OpenAI, Remote, etc.)
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @charivo/stt-core @charivo/core
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
### Basic Setup
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { createSTTManager } from "@charivo/stt-core";
|
|
24
|
+
import { createRemoteSTTTranscriber } from "@charivo/stt-transcriber-remote";
|
|
25
|
+
|
|
26
|
+
// Create a STT transcriber
|
|
27
|
+
const transcriber = createRemoteSTTTranscriber({
|
|
28
|
+
apiEndpoint: "/api/stt"
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Wrap with STTManager for event emission and coordination
|
|
32
|
+
const sttManager = createSTTManager(transcriber);
|
|
33
|
+
|
|
34
|
+
// Start recording (handled internally by transcriber)
|
|
35
|
+
await sttManager.start();
|
|
36
|
+
|
|
37
|
+
// Stop recording and get transcription
|
|
38
|
+
const transcription = await sttManager.stop();
|
|
39
|
+
console.log("User said:", transcription);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### With Event Bus
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { EventBus } from "@charivo/core";
|
|
46
|
+
|
|
47
|
+
const eventBus = new EventBus();
|
|
48
|
+
const sttManager = createSTTManager(transcriber);
|
|
49
|
+
|
|
50
|
+
// Connect event bus
|
|
51
|
+
sttManager.setEventEmitter({
|
|
52
|
+
emit: (event, data) => eventBus.emit(event, data)
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Listen to events
|
|
56
|
+
eventBus.on("stt:start", (data) => {
|
|
57
|
+
console.log("Recording started", data);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
eventBus.on("stt:stop", (data) => {
|
|
61
|
+
console.log("Transcription:", data.transcription);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
eventBus.on("stt:error", (data) => {
|
|
65
|
+
console.error("STT error:", data.error);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Start recording
|
|
69
|
+
await sttManager.start();
|
|
70
|
+
// → "stt:start" emitted
|
|
71
|
+
|
|
72
|
+
// Stop and transcribe
|
|
73
|
+
const text = await sttManager.stop();
|
|
74
|
+
// → Recording stops (handled by transcriber)
|
|
75
|
+
// → Audio is transcribed
|
|
76
|
+
// → "stt:stop" emitted with transcription
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Custom STT Transcriber
|
|
80
|
+
|
|
81
|
+
Each transcriber handles recording internally:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { STTTranscriber, STTOptions } from "@charivo/core";
|
|
85
|
+
import { MediaRecorderHelper, createSTTManager } from "@charivo/stt-core";
|
|
86
|
+
|
|
87
|
+
class MyCustomSTTTranscriber implements STTTranscriber {
|
|
88
|
+
private recorder = new MediaRecorderHelper();
|
|
89
|
+
private recordingOptions?: STTOptions;
|
|
90
|
+
|
|
91
|
+
async startRecording(options?: STTOptions): Promise<void> {
|
|
92
|
+
this.recordingOptions = options;
|
|
93
|
+
await this.recorder.start();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async stopRecording(): Promise<string> {
|
|
97
|
+
const audioBlob = await this.recorder.stop();
|
|
98
|
+
|
|
99
|
+
// Call your STT API
|
|
100
|
+
const formData = new FormData();
|
|
101
|
+
formData.append("audio", audioBlob);
|
|
102
|
+
if (this.recordingOptions?.language) {
|
|
103
|
+
formData.append("language", this.recordingOptions.language);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const response = await fetch("https://my-stt-api.com/transcribe", {
|
|
107
|
+
method: "POST",
|
|
108
|
+
body: formData
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const data = await response.json();
|
|
112
|
+
this.recordingOptions = undefined;
|
|
113
|
+
return data.transcription;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
isRecording(): boolean {
|
|
117
|
+
return this.recorder.isRecording();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const sttManager = createSTTManager(new MyCustomSTTTranscriber());
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Check Recording State
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
// Check if currently recording
|
|
128
|
+
if (sttManager.isRecording()) {
|
|
129
|
+
console.log("Recording in progress...");
|
|
130
|
+
} else {
|
|
131
|
+
console.log("Not recording");
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## API Reference
|
|
136
|
+
|
|
137
|
+
### `STTManager`
|
|
138
|
+
|
|
139
|
+
Main class for coordinating STT transcription and emitting events.
|
|
140
|
+
|
|
141
|
+
#### Constructor
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
new STTManager(transcriber: STTTranscriber)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Methods
|
|
148
|
+
|
|
149
|
+
##### `setEventEmitter(eventEmitter)`
|
|
150
|
+
Connect event emitter for STT event emission.
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
sttManager.setEventEmitter({
|
|
154
|
+
emit: (event, data) => { /* ... */ }
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
When set, the manager emits:
|
|
159
|
+
- `stt:start` with `{ options?: STTOptions }` when recording starts
|
|
160
|
+
- `stt:stop` with `{ transcription: string }` when transcription completes
|
|
161
|
+
- `stt:error` with `{ error: Error }` when an error occurs
|
|
162
|
+
|
|
163
|
+
##### `start(options?)`
|
|
164
|
+
Start audio recording (delegates to transcriber).
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
await sttManager.start();
|
|
168
|
+
|
|
169
|
+
// With language option
|
|
170
|
+
await sttManager.start({ language: "en-US" });
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The transcriber handles microphone access and recording internally.
|
|
174
|
+
|
|
175
|
+
##### `stop()`
|
|
176
|
+
Stop recording and get transcribed text (delegates to transcriber).
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
const transcription = await sttManager.stop();
|
|
180
|
+
console.log("User said:", transcription);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Returns the transcribed text as a string.
|
|
184
|
+
|
|
185
|
+
##### `isRecording()`
|
|
186
|
+
Check if currently recording (delegates to transcriber).
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
if (sttManager.isRecording()) {
|
|
190
|
+
console.log("Recording...");
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### `MediaRecorderHelper`
|
|
195
|
+
|
|
196
|
+
Shared utility for audio recording (used by blob-based transcribers).
|
|
197
|
+
|
|
198
|
+
#### Methods
|
|
199
|
+
|
|
200
|
+
##### `start()`
|
|
201
|
+
Start audio recording from microphone.
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
const recorder = new MediaRecorderHelper();
|
|
205
|
+
await recorder.start();
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
##### `stop()`
|
|
209
|
+
Stop recording and return audio blob.
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
const audioBlob = await recorder.stop();
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
##### `isRecording()`
|
|
216
|
+
Check if currently recording.
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
if (recorder.isRecording()) {
|
|
220
|
+
console.log("Recording...");
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
##### `abort()`
|
|
225
|
+
Abort recording immediately without returning data.
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
recorder.abort();
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Events
|
|
232
|
+
|
|
233
|
+
### `stt:start`
|
|
234
|
+
|
|
235
|
+
Emitted when audio recording starts.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
{
|
|
239
|
+
options?: STTOptions
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Use this to:
|
|
244
|
+
- Show "recording" indicator
|
|
245
|
+
- Disable other audio inputs
|
|
246
|
+
- Update UI state
|
|
247
|
+
|
|
248
|
+
### `stt:stop`
|
|
249
|
+
|
|
250
|
+
Emitted when audio recording stops and transcription completes.
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
{
|
|
254
|
+
transcription: string
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Use this to:
|
|
259
|
+
- Display transcribed text
|
|
260
|
+
- Hide "recording" indicator
|
|
261
|
+
- Process user input
|
|
262
|
+
|
|
263
|
+
### `stt:error`
|
|
264
|
+
|
|
265
|
+
Emitted when an error occurs during recording or transcription.
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
{
|
|
269
|
+
error: Error
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Use this to:
|
|
274
|
+
- Show error message to user
|
|
275
|
+
- Clean up UI state
|
|
276
|
+
- Retry logic
|
|
277
|
+
|
|
278
|
+
## Integration with Charivo
|
|
279
|
+
|
|
280
|
+
The STT system integrates seamlessly with the Charivo framework:
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
import { Charivo } from "@charivo/core";
|
|
284
|
+
import { createSTTManager } from "@charivo/stt-core";
|
|
285
|
+
import { createWebSTTTranscriber } from "@charivo/stt-transcriber-web";
|
|
286
|
+
|
|
287
|
+
const charivo = new Charivo();
|
|
288
|
+
|
|
289
|
+
// Setup STT
|
|
290
|
+
const transcriber = createWebSTTTranscriber();
|
|
291
|
+
const sttManager = createSTTManager(transcriber);
|
|
292
|
+
charivo.attachSTT(sttManager);
|
|
293
|
+
|
|
294
|
+
// Start voice input
|
|
295
|
+
await sttManager.start();
|
|
296
|
+
|
|
297
|
+
// Stop and automatically send to character
|
|
298
|
+
const transcription = await sttManager.stop();
|
|
299
|
+
await charivo.userSay(transcription);
|
|
300
|
+
// → Character responds with voice and animation
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Architecture
|
|
304
|
+
|
|
305
|
+
```
|
|
306
|
+
STTManager (coordination layer)
|
|
307
|
+
├─ Event Emission
|
|
308
|
+
└─ STTTranscriber (handles recording internally)
|
|
309
|
+
├─ WebSTTTranscriber
|
|
310
|
+
│ └─ Web Speech API (real-time)
|
|
311
|
+
├─ OpenAISTTTranscriber
|
|
312
|
+
│ ├─ MediaRecorderHelper
|
|
313
|
+
│ └─ OpenAI Whisper API
|
|
314
|
+
└─ RemoteSTTTranscriber
|
|
315
|
+
├─ MediaRecorderHelper
|
|
316
|
+
└─ Your Server API
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Available Transcribers
|
|
320
|
+
|
|
321
|
+
### Web STT Transcriber (Free, Browser-native) ⭐ Recommended
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
pnpm add @charivo/stt-transcriber-web
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
import { createWebSTTTranscriber } from "@charivo/stt-transcriber-web";
|
|
329
|
+
|
|
330
|
+
const transcriber = createWebSTTTranscriber();
|
|
331
|
+
const sttManager = createSTTManager(transcriber);
|
|
332
|
+
|
|
333
|
+
// Works with STTManager!
|
|
334
|
+
await sttManager.start({ language: "en-US" });
|
|
335
|
+
const text = await sttManager.stop();
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Uses browser's built-in Web Speech API (no API key needed).
|
|
339
|
+
|
|
340
|
+
**Advantages:**
|
|
341
|
+
- 🆓 Completely free
|
|
342
|
+
- ⚡ Real-time recognition
|
|
343
|
+
- 🔒 No server required
|
|
344
|
+
- 🎯 Perfect for development and production
|
|
345
|
+
|
|
346
|
+
### Remote STT Transcriber (Production-ready)
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
pnpm add @charivo/stt-transcriber-remote
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
import { createRemoteSTTTranscriber } from "@charivo/stt-transcriber-remote";
|
|
354
|
+
|
|
355
|
+
const transcriber = createRemoteSTTTranscriber({
|
|
356
|
+
apiEndpoint: "/api/stt" // Your server endpoint
|
|
357
|
+
});
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Calls your server API to keep credentials secure.
|
|
361
|
+
|
|
362
|
+
### OpenAI STT Transcriber (Development/Testing Only)
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
pnpm add @charivo/stt-transcriber-openai
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
import { createOpenAISTTTranscriber } from "@charivo/stt-transcriber-openai";
|
|
370
|
+
|
|
371
|
+
const transcriber = createOpenAISTTTranscriber({
|
|
372
|
+
apiKey: "your-api-key", // ⚠️ Exposed on client
|
|
373
|
+
defaultLanguage: "en"
|
|
374
|
+
});
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
⚠️ **Warning**: API key is exposed on the client. Only use for development/testing.
|
|
378
|
+
|
|
379
|
+
## Browser Compatibility
|
|
380
|
+
|
|
381
|
+
STT transcribers use different browser APIs:
|
|
382
|
+
|
|
383
|
+
**MediaRecorderHelper** (OpenAI/Remote):
|
|
384
|
+
- Chrome/Edge 49+
|
|
385
|
+
- Firefox 29+
|
|
386
|
+
- Safari 14.1+
|
|
387
|
+
|
|
388
|
+
**Web Speech API** (Web):
|
|
389
|
+
- Chrome/Edge (fully supported)
|
|
390
|
+
- Safari (limited support)
|
|
391
|
+
- Firefox (not supported)
|
|
392
|
+
|
|
393
|
+
## Error Handling
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
try {
|
|
397
|
+
await sttManager.start();
|
|
398
|
+
const transcription = await sttManager.stop();
|
|
399
|
+
} catch (error) {
|
|
400
|
+
if (error.name === "NotAllowedError") {
|
|
401
|
+
console.error("Microphone permission denied");
|
|
402
|
+
} else if (error.name === "NotFoundError") {
|
|
403
|
+
console.error("No microphone found");
|
|
404
|
+
} else {
|
|
405
|
+
console.error("STT error:", error);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Common errors:
|
|
411
|
+
- `NotAllowedError` - User denied microphone permission
|
|
412
|
+
- `NotFoundError` - No microphone device available
|
|
413
|
+
- `NotReadableError` - Microphone is already in use
|
|
414
|
+
- Network errors - Transcription API failed
|
|
415
|
+
|
|
416
|
+
## Best Practices
|
|
417
|
+
|
|
418
|
+
1. **Use Web STT for most cases**: Free, fast, and browser-native
|
|
419
|
+
2. **Request permission early**: Test microphone access before starting recording
|
|
420
|
+
3. **Show recording indicator**: Always show visual feedback when recording
|
|
421
|
+
4. **Handle errors gracefully**: Provide clear error messages to users
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
// React example
|
|
425
|
+
function VoiceInput() {
|
|
426
|
+
const [recording, setRecording] = useState(false);
|
|
427
|
+
const [error, setError] = useState<string | null>(null);
|
|
428
|
+
|
|
429
|
+
const handleStart = async () => {
|
|
430
|
+
try {
|
|
431
|
+
setError(null);
|
|
432
|
+
await sttManager.start();
|
|
433
|
+
setRecording(true);
|
|
434
|
+
} catch (err) {
|
|
435
|
+
setError("Failed to start recording");
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const handleStop = async () => {
|
|
440
|
+
try {
|
|
441
|
+
const text = await sttManager.stop();
|
|
442
|
+
setRecording(false);
|
|
443
|
+
onTranscription(text);
|
|
444
|
+
} catch (err) {
|
|
445
|
+
setError("Failed to transcribe");
|
|
446
|
+
setRecording(false);
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
return (
|
|
451
|
+
<div>
|
|
452
|
+
<button onClick={recording ? handleStop : handleStart}>
|
|
453
|
+
{recording ? "Stop" : "Start"} Recording
|
|
454
|
+
</button>
|
|
455
|
+
{error && <div className="error">{error}</div>}
|
|
456
|
+
</div>
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
## License
|
|
462
|
+
|
|
463
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { STTManager, STTTranscriber, STTOptions } from '@charivo/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* STT Manager - Manages STT session state
|
|
5
|
+
*
|
|
6
|
+
* Simplified design: Delegates to STTTranscriber
|
|
7
|
+
* - STTTranscriber handles recording internally
|
|
8
|
+
* - Manager only handles event emission
|
|
9
|
+
* - Thin wrapper for consistent API
|
|
10
|
+
*/
|
|
11
|
+
declare class STTManagerImpl implements STTManager {
|
|
12
|
+
private sttTranscriber;
|
|
13
|
+
private eventEmitter?;
|
|
14
|
+
constructor(sttTranscriber: STTTranscriber);
|
|
15
|
+
/**
|
|
16
|
+
* Set event emitter for STT events
|
|
17
|
+
*/
|
|
18
|
+
setEventEmitter(eventEmitter: {
|
|
19
|
+
emit: (event: string, data: any) => void;
|
|
20
|
+
}): void;
|
|
21
|
+
/**
|
|
22
|
+
* Start audio recording (delegates to transcriber)
|
|
23
|
+
*/
|
|
24
|
+
start(options?: STTOptions): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Stop audio recording and get transcription (delegates to transcriber)
|
|
27
|
+
*/
|
|
28
|
+
stop(): Promise<string>;
|
|
29
|
+
/**
|
|
30
|
+
* Check if currently recording (delegates to transcriber)
|
|
31
|
+
*/
|
|
32
|
+
isRecording(): boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Helper function to create STT Manager
|
|
36
|
+
*/
|
|
37
|
+
declare function createSTTManager(sttTranscriber: STTTranscriber): STTManager;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* MediaRecorderHelper - Browser audio recording utility
|
|
41
|
+
*
|
|
42
|
+
* Handles MediaRecorder lifecycle:
|
|
43
|
+
* - Start/stop recording
|
|
44
|
+
* - Audio chunk collection
|
|
45
|
+
* - Stream cleanup
|
|
46
|
+
* - Blob generation
|
|
47
|
+
*/
|
|
48
|
+
declare class MediaRecorderHelper {
|
|
49
|
+
private mediaRecorder;
|
|
50
|
+
private audioChunks;
|
|
51
|
+
private recording;
|
|
52
|
+
/**
|
|
53
|
+
* Start audio recording from user's microphone
|
|
54
|
+
*/
|
|
55
|
+
start(): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Stop audio recording and return the recorded audio as a Blob
|
|
58
|
+
*/
|
|
59
|
+
stop(): Promise<Blob>;
|
|
60
|
+
/**
|
|
61
|
+
* Check if currently recording
|
|
62
|
+
*/
|
|
63
|
+
isRecording(): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Abort recording immediately without returning data
|
|
66
|
+
*/
|
|
67
|
+
abort(): void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { MediaRecorderHelper, STTManagerImpl, createSTTManager };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { STTManager, STTTranscriber, STTOptions } from '@charivo/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* STT Manager - Manages STT session state
|
|
5
|
+
*
|
|
6
|
+
* Simplified design: Delegates to STTTranscriber
|
|
7
|
+
* - STTTranscriber handles recording internally
|
|
8
|
+
* - Manager only handles event emission
|
|
9
|
+
* - Thin wrapper for consistent API
|
|
10
|
+
*/
|
|
11
|
+
declare class STTManagerImpl implements STTManager {
|
|
12
|
+
private sttTranscriber;
|
|
13
|
+
private eventEmitter?;
|
|
14
|
+
constructor(sttTranscriber: STTTranscriber);
|
|
15
|
+
/**
|
|
16
|
+
* Set event emitter for STT events
|
|
17
|
+
*/
|
|
18
|
+
setEventEmitter(eventEmitter: {
|
|
19
|
+
emit: (event: string, data: any) => void;
|
|
20
|
+
}): void;
|
|
21
|
+
/**
|
|
22
|
+
* Start audio recording (delegates to transcriber)
|
|
23
|
+
*/
|
|
24
|
+
start(options?: STTOptions): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Stop audio recording and get transcription (delegates to transcriber)
|
|
27
|
+
*/
|
|
28
|
+
stop(): Promise<string>;
|
|
29
|
+
/**
|
|
30
|
+
* Check if currently recording (delegates to transcriber)
|
|
31
|
+
*/
|
|
32
|
+
isRecording(): boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Helper function to create STT Manager
|
|
36
|
+
*/
|
|
37
|
+
declare function createSTTManager(sttTranscriber: STTTranscriber): STTManager;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* MediaRecorderHelper - Browser audio recording utility
|
|
41
|
+
*
|
|
42
|
+
* Handles MediaRecorder lifecycle:
|
|
43
|
+
* - Start/stop recording
|
|
44
|
+
* - Audio chunk collection
|
|
45
|
+
* - Stream cleanup
|
|
46
|
+
* - Blob generation
|
|
47
|
+
*/
|
|
48
|
+
declare class MediaRecorderHelper {
|
|
49
|
+
private mediaRecorder;
|
|
50
|
+
private audioChunks;
|
|
51
|
+
private recording;
|
|
52
|
+
/**
|
|
53
|
+
* Start audio recording from user's microphone
|
|
54
|
+
*/
|
|
55
|
+
start(): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Stop audio recording and return the recorded audio as a Blob
|
|
58
|
+
*/
|
|
59
|
+
stop(): Promise<Blob>;
|
|
60
|
+
/**
|
|
61
|
+
* Check if currently recording
|
|
62
|
+
*/
|
|
63
|
+
isRecording(): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Abort recording immediately without returning data
|
|
66
|
+
*/
|
|
67
|
+
abort(): void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { MediaRecorderHelper, STTManagerImpl, createSTTManager };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
MediaRecorderHelper: () => MediaRecorderHelper,
|
|
24
|
+
STTManagerImpl: () => STTManagerImpl,
|
|
25
|
+
createSTTManager: () => createSTTManager
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/stt-manager.ts
|
|
30
|
+
var STTManagerImpl = class {
|
|
31
|
+
sttTranscriber;
|
|
32
|
+
eventEmitter;
|
|
33
|
+
constructor(sttTranscriber) {
|
|
34
|
+
this.sttTranscriber = sttTranscriber;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Set event emitter for STT events
|
|
38
|
+
*/
|
|
39
|
+
setEventEmitter(eventEmitter) {
|
|
40
|
+
console.log("\u{1F517} STT Manager: Event emitter connected");
|
|
41
|
+
this.eventEmitter = eventEmitter;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Start audio recording (delegates to transcriber)
|
|
45
|
+
*/
|
|
46
|
+
async start(options) {
|
|
47
|
+
console.log("\u{1F3A4} STT Manager: Starting recording", options);
|
|
48
|
+
this.eventEmitter?.emit("stt:start", { options });
|
|
49
|
+
try {
|
|
50
|
+
await this.sttTranscriber.startRecording(options);
|
|
51
|
+
console.log("\u2705 STT Manager: Recording started");
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error("\u274C STT Manager: Recording failed", error);
|
|
54
|
+
this.eventEmitter?.emit("stt:error", { error });
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Stop audio recording and get transcription (delegates to transcriber)
|
|
60
|
+
*/
|
|
61
|
+
async stop() {
|
|
62
|
+
console.log("\u{1F6D1} STT Manager: Stopping recording");
|
|
63
|
+
try {
|
|
64
|
+
const transcription = await this.sttTranscriber.stopRecording();
|
|
65
|
+
console.log("\u2705 STT Manager: Transcription completed", transcription);
|
|
66
|
+
this.eventEmitter?.emit("stt:stop", { transcription });
|
|
67
|
+
return transcription;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error("\u274C STT Manager: Transcription failed", error);
|
|
70
|
+
this.eventEmitter?.emit("stt:error", { error });
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Check if currently recording (delegates to transcriber)
|
|
76
|
+
*/
|
|
77
|
+
isRecording() {
|
|
78
|
+
return this.sttTranscriber.isRecording();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
function createSTTManager(sttTranscriber) {
|
|
82
|
+
return new STTManagerImpl(sttTranscriber);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/media-recorder-helper.ts
|
|
86
|
+
var MediaRecorderHelper = class {
|
|
87
|
+
mediaRecorder = null;
|
|
88
|
+
audioChunks = [];
|
|
89
|
+
recording = false;
|
|
90
|
+
/**
|
|
91
|
+
* Start audio recording from user's microphone
|
|
92
|
+
*/
|
|
93
|
+
async start() {
|
|
94
|
+
if (this.recording) {
|
|
95
|
+
throw new Error("Already recording");
|
|
96
|
+
}
|
|
97
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
98
|
+
this.mediaRecorder = new MediaRecorder(stream);
|
|
99
|
+
this.audioChunks = [];
|
|
100
|
+
this.mediaRecorder.ondataavailable = (event) => {
|
|
101
|
+
if (event.data.size > 0) {
|
|
102
|
+
this.audioChunks.push(event.data);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
this.mediaRecorder.start();
|
|
106
|
+
this.recording = true;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Stop audio recording and return the recorded audio as a Blob
|
|
110
|
+
*/
|
|
111
|
+
async stop() {
|
|
112
|
+
if (!this.recording || !this.mediaRecorder) {
|
|
113
|
+
throw new Error("Not recording");
|
|
114
|
+
}
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
this.mediaRecorder.onstop = () => {
|
|
117
|
+
const audioBlob = new Blob(this.audioChunks, { type: "audio/webm" });
|
|
118
|
+
this.audioChunks = [];
|
|
119
|
+
this.mediaRecorder?.stream.getTracks().forEach((track) => track.stop());
|
|
120
|
+
this.mediaRecorder = null;
|
|
121
|
+
this.recording = false;
|
|
122
|
+
resolve(audioBlob);
|
|
123
|
+
};
|
|
124
|
+
this.mediaRecorder.onerror = (error) => {
|
|
125
|
+
this.recording = false;
|
|
126
|
+
reject(error);
|
|
127
|
+
};
|
|
128
|
+
this.mediaRecorder.stop();
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Check if currently recording
|
|
133
|
+
*/
|
|
134
|
+
isRecording() {
|
|
135
|
+
return this.recording;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Abort recording immediately without returning data
|
|
139
|
+
*/
|
|
140
|
+
abort() {
|
|
141
|
+
if (this.mediaRecorder) {
|
|
142
|
+
this.mediaRecorder.stream.getTracks().forEach((track) => track.stop());
|
|
143
|
+
this.mediaRecorder = null;
|
|
144
|
+
this.audioChunks = [];
|
|
145
|
+
this.recording = false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
150
|
+
0 && (module.exports = {
|
|
151
|
+
MediaRecorderHelper,
|
|
152
|
+
STTManagerImpl,
|
|
153
|
+
createSTTManager
|
|
154
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// src/stt-manager.ts
|
|
2
|
+
var STTManagerImpl = class {
|
|
3
|
+
sttTranscriber;
|
|
4
|
+
eventEmitter;
|
|
5
|
+
constructor(sttTranscriber) {
|
|
6
|
+
this.sttTranscriber = sttTranscriber;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Set event emitter for STT events
|
|
10
|
+
*/
|
|
11
|
+
setEventEmitter(eventEmitter) {
|
|
12
|
+
console.log("\u{1F517} STT Manager: Event emitter connected");
|
|
13
|
+
this.eventEmitter = eventEmitter;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Start audio recording (delegates to transcriber)
|
|
17
|
+
*/
|
|
18
|
+
async start(options) {
|
|
19
|
+
console.log("\u{1F3A4} STT Manager: Starting recording", options);
|
|
20
|
+
this.eventEmitter?.emit("stt:start", { options });
|
|
21
|
+
try {
|
|
22
|
+
await this.sttTranscriber.startRecording(options);
|
|
23
|
+
console.log("\u2705 STT Manager: Recording started");
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error("\u274C STT Manager: Recording failed", error);
|
|
26
|
+
this.eventEmitter?.emit("stt:error", { error });
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Stop audio recording and get transcription (delegates to transcriber)
|
|
32
|
+
*/
|
|
33
|
+
async stop() {
|
|
34
|
+
console.log("\u{1F6D1} STT Manager: Stopping recording");
|
|
35
|
+
try {
|
|
36
|
+
const transcription = await this.sttTranscriber.stopRecording();
|
|
37
|
+
console.log("\u2705 STT Manager: Transcription completed", transcription);
|
|
38
|
+
this.eventEmitter?.emit("stt:stop", { transcription });
|
|
39
|
+
return transcription;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error("\u274C STT Manager: Transcription failed", error);
|
|
42
|
+
this.eventEmitter?.emit("stt:error", { error });
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Check if currently recording (delegates to transcriber)
|
|
48
|
+
*/
|
|
49
|
+
isRecording() {
|
|
50
|
+
return this.sttTranscriber.isRecording();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
function createSTTManager(sttTranscriber) {
|
|
54
|
+
return new STTManagerImpl(sttTranscriber);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/media-recorder-helper.ts
|
|
58
|
+
var MediaRecorderHelper = class {
|
|
59
|
+
mediaRecorder = null;
|
|
60
|
+
audioChunks = [];
|
|
61
|
+
recording = false;
|
|
62
|
+
/**
|
|
63
|
+
* Start audio recording from user's microphone
|
|
64
|
+
*/
|
|
65
|
+
async start() {
|
|
66
|
+
if (this.recording) {
|
|
67
|
+
throw new Error("Already recording");
|
|
68
|
+
}
|
|
69
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
70
|
+
this.mediaRecorder = new MediaRecorder(stream);
|
|
71
|
+
this.audioChunks = [];
|
|
72
|
+
this.mediaRecorder.ondataavailable = (event) => {
|
|
73
|
+
if (event.data.size > 0) {
|
|
74
|
+
this.audioChunks.push(event.data);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
this.mediaRecorder.start();
|
|
78
|
+
this.recording = true;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Stop audio recording and return the recorded audio as a Blob
|
|
82
|
+
*/
|
|
83
|
+
async stop() {
|
|
84
|
+
if (!this.recording || !this.mediaRecorder) {
|
|
85
|
+
throw new Error("Not recording");
|
|
86
|
+
}
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
this.mediaRecorder.onstop = () => {
|
|
89
|
+
const audioBlob = new Blob(this.audioChunks, { type: "audio/webm" });
|
|
90
|
+
this.audioChunks = [];
|
|
91
|
+
this.mediaRecorder?.stream.getTracks().forEach((track) => track.stop());
|
|
92
|
+
this.mediaRecorder = null;
|
|
93
|
+
this.recording = false;
|
|
94
|
+
resolve(audioBlob);
|
|
95
|
+
};
|
|
96
|
+
this.mediaRecorder.onerror = (error) => {
|
|
97
|
+
this.recording = false;
|
|
98
|
+
reject(error);
|
|
99
|
+
};
|
|
100
|
+
this.mediaRecorder.stop();
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Check if currently recording
|
|
105
|
+
*/
|
|
106
|
+
isRecording() {
|
|
107
|
+
return this.recording;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Abort recording immediately without returning data
|
|
111
|
+
*/
|
|
112
|
+
abort() {
|
|
113
|
+
if (this.mediaRecorder) {
|
|
114
|
+
this.mediaRecorder.stream.getTracks().forEach((track) => track.stop());
|
|
115
|
+
this.mediaRecorder = null;
|
|
116
|
+
this.audioChunks = [];
|
|
117
|
+
this.recording = false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
export {
|
|
122
|
+
MediaRecorderHelper,
|
|
123
|
+
STTManagerImpl,
|
|
124
|
+
createSTTManager
|
|
125
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@charivo/stt-core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Core STT functionality for Charivo framework",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"charivo",
|
|
17
|
+
"stt",
|
|
18
|
+
"speech-to-text",
|
|
19
|
+
"audio",
|
|
20
|
+
"transcription"
|
|
21
|
+
],
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@charivo/core": "0.0.1"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@charivo/shared": "0.0.1"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/zeikar/charivo.git",
|
|
39
|
+
"directory": "packages/stt-core"
|
|
40
|
+
},
|
|
41
|
+
"author": {
|
|
42
|
+
"name": "Zeikar",
|
|
43
|
+
"url": "https://github.com/zeikar"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/zeikar/charivo#readme",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/zeikar/charivo/issues"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsup",
|
|
51
|
+
"dev": "tsup --watch",
|
|
52
|
+
"test": "vitest",
|
|
53
|
+
"clean": "rm -rf dist"
|
|
54
|
+
}
|
|
55
|
+
}
|