@corti/dictation-web 0.4.0-rc.2 → 0.5.0-rc
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/README.md +53 -3
- package/dist/bundle.js +795 -152
- package/dist/components/corti-dictation.d.ts +15 -1
- package/dist/components/corti-dictation.js +35 -0
- package/dist/components/corti-dictation.js.map +1 -1
- package/dist/components/device-selector.d.ts +1 -1
- package/dist/components/keybinding-selector.d.ts +14 -0
- package/dist/components/keybinding-selector.js +82 -0
- package/dist/components/keybinding-selector.js.map +1 -0
- package/dist/components/language-selector.d.ts +1 -1
- package/dist/components/mode-selector.d.ts +14 -0
- package/dist/components/mode-selector.js +73 -0
- package/dist/components/mode-selector.js.map +1 -0
- package/dist/components/recording-button.d.ts +5 -2
- package/dist/components/recording-button.js +83 -17
- package/dist/components/recording-button.js.map +1 -1
- package/dist/components/settings-menu.d.ts +2 -0
- package/dist/components/settings-menu.js +20 -0
- package/dist/components/settings-menu.js.map +1 -1
- package/dist/contexts/dictation-context.d.ts +9 -1
- package/dist/contexts/dictation-context.js +35 -2
- package/dist/contexts/dictation-context.js.map +1 -1
- package/dist/controllers/dictation-controller.d.ts +2 -1
- package/dist/controllers/dictation-controller.js +53 -19
- package/dist/controllers/dictation-controller.js.map +1 -1
- package/dist/controllers/keybinding-controller.d.ts +18 -0
- package/dist/controllers/keybinding-controller.js +86 -0
- package/dist/controllers/keybinding-controller.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/package.json +87 -7
- package/dist/styles/component-styles.d.ts +1 -0
- package/dist/styles/component-styles.js +10 -0
- package/dist/styles/component-styles.js.map +1 -1
- package/dist/styles/keybinding-selector.d.ts +2 -0
- package/dist/styles/keybinding-selector.js +72 -0
- package/dist/styles/keybinding-selector.js.map +1 -0
- package/dist/styles/mode-selector.d.ts +2 -0
- package/dist/styles/mode-selector.js +56 -0
- package/dist/styles/mode-selector.js.map +1 -0
- package/dist/styles/select.d.ts +1 -1
- package/dist/styles/select.js +6 -10
- package/dist/styles/select.js.map +1 -1
- package/dist/styles/settings-menu.js +9 -1
- package/dist/styles/settings-menu.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types.d.ts +3 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/events.d.ts +14 -1
- package/dist/utils/events.js +22 -0
- package/dist/utils/events.js.map +1 -1
- package/dist/utils/keybinding.d.ts +36 -0
- package/dist/utils/keybinding.js +125 -0
- package/dist/utils/keybinding.js.map +1 -0
- package/package.json +2 -2
- package/dist/CortiDictation.d.ts +0 -55
- package/dist/CortiDictation.js +0 -303
- package/dist/CortiDictation.js.map +0 -1
- package/dist/DictationService.d.ts +0 -16
- package/dist/DictationService.js +0 -88
- package/dist/DictationService.js.map +0 -1
- package/dist/RecorderManager.d.ts +0 -25
- package/dist/RecorderManager.js +0 -145
- package/dist/RecorderManager.js.map +0 -1
- package/dist/audioService.d.ts +0 -6
- package/dist/audioService.js +0 -21
- package/dist/audioService.js.map +0 -1
- package/dist/controllers/DictationController.d.ts +0 -35
- package/dist/controllers/DictationController.js +0 -130
- package/dist/controllers/DictationController.js.map +0 -1
- package/dist/controllers/MediaController.d.ts +0 -31
- package/dist/controllers/MediaController.js +0 -99
- package/dist/controllers/MediaController.js.map +0 -1
- package/dist/src/components/audio-visualiser.d.ts +0 -14
- package/dist/src/components/audio-visualiser.js +0 -57
- package/dist/src/components/audio-visualiser.js.map +0 -1
- package/dist/src/components/corti-dictation.d.ts +0 -123
- package/dist/src/components/corti-dictation.js +0 -224
- package/dist/src/components/corti-dictation.js.map +0 -1
- package/dist/src/components/device-selector.d.ts +0 -24
- package/dist/src/components/device-selector.js +0 -106
- package/dist/src/components/device-selector.js.map +0 -1
- package/dist/src/components/language-selector.d.ts +0 -24
- package/dist/src/components/language-selector.js +0 -100
- package/dist/src/components/language-selector.js.map +0 -1
- package/dist/src/components/recording-button.d.ts +0 -37
- package/dist/src/components/recording-button.js +0 -203
- package/dist/src/components/recording-button.js.map +0 -1
- package/dist/src/components/settings-menu.d.ts +0 -16
- package/dist/src/components/settings-menu.js +0 -80
- package/dist/src/components/settings-menu.js.map +0 -1
- package/dist/src/constants.d.ts +0 -4
- package/dist/src/constants.js +0 -37
- package/dist/src/constants.js.map +0 -1
- package/dist/src/contexts/dictation-context.d.ts +0 -97
- package/dist/src/contexts/dictation-context.js +0 -208
- package/dist/src/contexts/dictation-context.js.map +0 -1
- package/dist/src/controllers/DictationController.d.ts +0 -35
- package/dist/src/controllers/DictationController.js +0 -130
- package/dist/src/controllers/DictationController.js.map +0 -1
- package/dist/src/controllers/MediaController.d.ts +0 -31
- package/dist/src/controllers/MediaController.js +0 -99
- package/dist/src/controllers/MediaController.js.map +0 -1
- package/dist/src/icons/icons.d.ts +0 -17
- package/dist/src/icons/icons.js +0 -158
- package/dist/src/icons/icons.js.map +0 -1
- package/dist/src/styles/ComponentStyles.d.ts +0 -2
- package/dist/src/styles/ComponentStyles.js +0 -18
- package/dist/src/styles/ComponentStyles.js.map +0 -1
- package/dist/src/styles/audio-visualiser.d.ts +0 -2
- package/dist/src/styles/audio-visualiser.js +0 -33
- package/dist/src/styles/audio-visualiser.js.map +0 -1
- package/dist/src/styles/buttons.d.ts +0 -2
- package/dist/src/styles/buttons.js +0 -52
- package/dist/src/styles/buttons.js.map +0 -1
- package/dist/src/styles/callout.d.ts +0 -2
- package/dist/src/styles/callout.js +0 -23
- package/dist/src/styles/callout.js.map +0 -1
- package/dist/src/styles/default-theme.d.ts +0 -2
- package/dist/src/styles/default-theme.js +0 -50
- package/dist/src/styles/default-theme.js.map +0 -1
- package/dist/src/styles/recording-button.d.ts +0 -2
- package/dist/src/styles/recording-button.js +0 -8
- package/dist/src/styles/recording-button.js.map +0 -1
- package/dist/src/styles/select.d.ts +0 -2
- package/dist/src/styles/select.js +0 -36
- package/dist/src/styles/select.js.map +0 -1
- package/dist/src/styles/settings-menu.d.ts +0 -2
- package/dist/src/styles/settings-menu.js +0 -34
- package/dist/src/styles/settings-menu.js.map +0 -1
- package/dist/src/types.d.ts +0 -7
- package/dist/src/types.js +0 -2
- package/dist/src/types.js.map +0 -1
- package/dist/src/utils/auth.d.ts +0 -9
- package/dist/src/utils/auth.js +0 -21
- package/dist/src/utils/auth.js.map +0 -1
- package/dist/src/utils/converters.d.ts +0 -4
- package/dist/src/utils/converters.js +0 -8
- package/dist/src/utils/converters.js.map +0 -1
- package/dist/src/utils/devices.d.ts +0 -26
- package/dist/src/utils/devices.js +0 -53
- package/dist/src/utils/devices.js.map +0 -1
- package/dist/src/utils/events.d.ts +0 -44
- package/dist/src/utils/events.js +0 -88
- package/dist/src/utils/events.js.map +0 -1
- package/dist/src/utils/languages.d.ts +0 -7
- package/dist/src/utils/languages.js +0 -29
- package/dist/src/utils/languages.js.map +0 -1
- package/dist/src/utils/media.d.ts +0 -6
- package/dist/src/utils/media.js +0 -39
- package/dist/src/utils/media.js.map +0 -1
- package/dist/src/utils/token.d.ts +0 -13
- package/dist/src/utils/token.js +0 -60
- package/dist/src/utils/token.js.map +0 -1
- package/dist/src/utils/validation.d.ts +0 -1
- package/dist/src/utils/validation.js +0 -7
- package/dist/src/utils/validation.js.map +0 -1
- package/dist/stories/audio-visualiser.stories.d.ts +0 -39
- package/dist/stories/audio-visualiser.stories.js +0 -71
- package/dist/stories/audio-visualiser.stories.js.map +0 -1
- package/dist/stories/corti-dictation.stories.d.ts +0 -27
- package/dist/stories/corti-dictation.stories.js +0 -129
- package/dist/stories/corti-dictation.stories.js.map +0 -1
- package/dist/stories/device-selector.stories.d.ts +0 -18
- package/dist/stories/device-selector.stories.js +0 -84
- package/dist/stories/device-selector.stories.js.map +0 -1
- package/dist/stories/language-selector.stories.d.ts +0 -18
- package/dist/stories/language-selector.stories.js +0 -53
- package/dist/stories/language-selector.stories.js.map +0 -1
- package/dist/stories/recording-button.stories.d.ts +0 -27
- package/dist/stories/recording-button.stories.js +0 -90
- package/dist/stories/recording-button.stories.js.map +0 -1
- package/dist/stories/settings-menu.stories.d.ts +0 -23
- package/dist/stories/settings-menu.stories.js +0 -156
- package/dist/stories/settings-menu.stories.js.map +0 -1
- package/dist/styles/ComponentStyles.d.ts +0 -2
- package/dist/styles/ComponentStyles.js +0 -18
- package/dist/styles/ComponentStyles.js.map +0 -1
- package/dist/styles/default-theme.d.ts +0 -2
- package/dist/styles/default-theme.js +0 -14
- package/dist/styles/default-theme.js.map +0 -1
- package/dist/styles/theme.d.ts +0 -2
- package/dist/styles/theme.js +0 -56
- package/dist/styles/theme.js.map +0 -1
- package/dist/tsconfig.stories.tsbuildinfo +0 -1
- package/dist/utils.d.ts +0 -59
- package/dist/utils.js +0 -179
- package/dist/utils.js.map +0 -1
package/dist/DictationService.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { CortiClient } from '@corti/sdk';
|
|
2
|
-
import { getErrorMessage } from './utils.js';
|
|
3
|
-
export class DictationService extends EventTarget {
|
|
4
|
-
constructor(mediaStream, { dictationConfig, serverConfig, }) {
|
|
5
|
-
super();
|
|
6
|
-
this.mediaRecorder = new MediaRecorder(mediaStream);
|
|
7
|
-
this.serverConfig = serverConfig;
|
|
8
|
-
this.dictationConfig = dictationConfig;
|
|
9
|
-
// We're forced to remove from expiresIn/refreshExpiresIn here,
|
|
10
|
-
// because we recreate connection every time we connect
|
|
11
|
-
// => it makes expiresIn (we receive from API) out of date
|
|
12
|
-
const { expiresIn, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
13
|
-
refreshExpiresIn, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
14
|
-
...authConfig } = serverConfig;
|
|
15
|
-
this.cortiClient = new CortiClient({
|
|
16
|
-
auth: authConfig,
|
|
17
|
-
});
|
|
18
|
-
this.mediaRecorder.ondataavailable = event => {
|
|
19
|
-
if (this.webSocket?.readyState === WebSocket.OPEN) {
|
|
20
|
-
this.webSocket.sendAudio(event.data);
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
dispatchCustomEvent(eventName, detail) {
|
|
25
|
-
this.dispatchEvent(new CustomEvent(eventName, {
|
|
26
|
-
detail,
|
|
27
|
-
bubbles: true,
|
|
28
|
-
composed: true,
|
|
29
|
-
}));
|
|
30
|
-
}
|
|
31
|
-
async startRecording() {
|
|
32
|
-
if (!this.serverConfig) {
|
|
33
|
-
this.dispatchEvent(new CustomEvent('error', {
|
|
34
|
-
detail: 'Invalid token',
|
|
35
|
-
bubbles: true,
|
|
36
|
-
composed: true,
|
|
37
|
-
}));
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
this.webSocket = await this.cortiClient.transcribe.connect({
|
|
41
|
-
configuration: this.dictationConfig,
|
|
42
|
-
});
|
|
43
|
-
this.webSocket.on('message', message => {
|
|
44
|
-
switch (message.type) {
|
|
45
|
-
case 'CONFIG_ACCEPTED':
|
|
46
|
-
this.mediaRecorder.start(250);
|
|
47
|
-
break;
|
|
48
|
-
case 'transcript':
|
|
49
|
-
this.dispatchCustomEvent('transcript', message);
|
|
50
|
-
break;
|
|
51
|
-
case 'command':
|
|
52
|
-
this.dispatchCustomEvent('command', message);
|
|
53
|
-
break;
|
|
54
|
-
case 'usage':
|
|
55
|
-
this.dispatchCustomEvent('usage', message);
|
|
56
|
-
break;
|
|
57
|
-
default:
|
|
58
|
-
console.warn(`Unhandled message type: ${message.type}`);
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
this.webSocket.on('error', event => {
|
|
63
|
-
this.stopRecording();
|
|
64
|
-
this.dispatchCustomEvent('error', getErrorMessage(event));
|
|
65
|
-
});
|
|
66
|
-
this.webSocket.on('close', event => {
|
|
67
|
-
this.dispatchCustomEvent('stream-closed', event);
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
async stopRecording() {
|
|
71
|
-
this.mediaRecorder?.stop();
|
|
72
|
-
if (this.webSocket.readyState === WebSocket.OPEN) {
|
|
73
|
-
this.webSocket.sendEnd({
|
|
74
|
-
type: 'end',
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
const timeout = setTimeout(() => {
|
|
78
|
-
if (this.webSocket?.readyState === WebSocket.OPEN) {
|
|
79
|
-
this.webSocket.close();
|
|
80
|
-
}
|
|
81
|
-
}, 10000);
|
|
82
|
-
this.webSocket.on('close', (event) => {
|
|
83
|
-
clearTimeout(timeout);
|
|
84
|
-
this.dispatchCustomEvent('stream-closed', event);
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
//# sourceMappingURL=DictationService.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DictationService.js","sourceRoot":"","sources":["../src/DictationService.ts"],"names":[],"mappings":"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","sourcesContent":["import { CortiClient, Corti } from '@corti/sdk';\n\nimport type { ServerConfig } from './types.js';\nimport { getErrorMessage } from './utils.js';\n\ntype TranscribeSocket = Awaited<\n ReturnType<CortiClient['transcribe']['connect']>\n>;\n\nexport class DictationService extends EventTarget {\n private mediaRecorder: MediaRecorder;\n private webSocket!: TranscribeSocket;\n private serverConfig: ServerConfig;\n private dictationConfig: Corti.TranscribeConfig;\n private cortiClient: CortiClient;\n\n constructor(\n mediaStream: MediaStream,\n {\n dictationConfig,\n serverConfig,\n }: { dictationConfig: Corti.TranscribeConfig; serverConfig: ServerConfig },\n ) {\n super();\n this.mediaRecorder = new MediaRecorder(mediaStream);\n this.serverConfig = serverConfig;\n this.dictationConfig = dictationConfig;\n\n // We're forced to remove from expiresIn/refreshExpiresIn here,\n // because we recreate connection every time we connect\n // => it makes expiresIn (we receive from API) out of date\n const {\n expiresIn, // eslint-disable-line @typescript-eslint/no-unused-vars\n refreshExpiresIn, // eslint-disable-line @typescript-eslint/no-unused-vars\n ...authConfig\n } = serverConfig;\n\n this.cortiClient = new CortiClient({\n auth: authConfig,\n });\n\n this.mediaRecorder.ondataavailable = event => {\n if (this.webSocket?.readyState === WebSocket.OPEN) {\n this.webSocket.sendAudio(event.data);\n }\n };\n }\n\n private dispatchCustomEvent(eventName: string, detail?: unknown): void {\n this.dispatchEvent(\n new CustomEvent(eventName, {\n detail,\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n public async startRecording() {\n if (!this.serverConfig) {\n this.dispatchEvent(\n new CustomEvent('error', {\n detail: 'Invalid token',\n bubbles: true,\n composed: true,\n }),\n );\n return;\n }\n\n this.webSocket = await this.cortiClient.transcribe.connect({\n configuration: this.dictationConfig,\n });\n\n this.webSocket.on('message', message => {\n switch (message.type) {\n case 'CONFIG_ACCEPTED':\n this.mediaRecorder.start(250);\n break;\n case 'transcript':\n this.dispatchCustomEvent('transcript', message);\n break;\n case 'command':\n this.dispatchCustomEvent('command', message);\n break;\n case 'usage':\n this.dispatchCustomEvent('usage', message);\n break;\n default:\n console.warn(`Unhandled message type: ${message.type}`);\n break;\n }\n });\n\n this.webSocket.on('error', event => {\n this.stopRecording();\n\n this.dispatchCustomEvent('error', getErrorMessage(event));\n });\n\n this.webSocket.on('close', event => {\n this.dispatchCustomEvent('stream-closed', event);\n });\n }\n\n public async stopRecording(): Promise<void> {\n this.mediaRecorder?.stop();\n\n if (this.webSocket.readyState === WebSocket.OPEN) {\n this.webSocket.sendEnd({\n type: 'end',\n });\n }\n\n const timeout: NodeJS.Timeout = setTimeout(() => {\n if (this.webSocket?.readyState === WebSocket.OPEN) {\n this.webSocket.close();\n }\n }, 10000);\n\n this.webSocket.on('close', (event) => {\n clearTimeout(timeout);\n this.dispatchCustomEvent('stream-closed', event);\n });\n }\n}\n"]}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Corti } from '@corti/sdk';
|
|
2
|
-
import type { RecordingState, ServerConfig } from './types.js';
|
|
3
|
-
export declare class RecorderManager extends EventTarget {
|
|
4
|
-
devices: MediaDeviceInfo[];
|
|
5
|
-
selectedDevice: MediaDeviceInfo | undefined;
|
|
6
|
-
recordingState: RecordingState;
|
|
7
|
-
private _mediaStream;
|
|
8
|
-
private _audioService;
|
|
9
|
-
private _dictationService;
|
|
10
|
-
private _visualiserInterval?;
|
|
11
|
-
constructor();
|
|
12
|
-
initialize(): Promise<{
|
|
13
|
-
devices: MediaDeviceInfo[];
|
|
14
|
-
selectedDevice: MediaDeviceInfo | undefined;
|
|
15
|
-
}>;
|
|
16
|
-
private dispatchCustomEvent;
|
|
17
|
-
private handleDevicesChange;
|
|
18
|
-
startRecording(params: {
|
|
19
|
-
dictationConfig: Corti.TranscribeConfig;
|
|
20
|
-
serverConfig: ServerConfig;
|
|
21
|
-
debug_displayAudio?: boolean;
|
|
22
|
-
}): Promise<void>;
|
|
23
|
-
stopRecording(): Promise<void>;
|
|
24
|
-
private _updateRecordingState;
|
|
25
|
-
}
|
package/dist/RecorderManager.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { getAudioDevices, getMediaStream } from './utils.js';
|
|
2
|
-
import { AudioService } from './audioService.js';
|
|
3
|
-
import { DictationService } from './DictationService.js';
|
|
4
|
-
export class RecorderManager extends EventTarget {
|
|
5
|
-
constructor() {
|
|
6
|
-
super();
|
|
7
|
-
this.devices = [];
|
|
8
|
-
this.recordingState = 'stopped';
|
|
9
|
-
this._mediaStream = null;
|
|
10
|
-
this._audioService = null;
|
|
11
|
-
this._dictationService = null;
|
|
12
|
-
navigator.mediaDevices.addEventListener('devicechange', this.handleDevicesChange.bind(this));
|
|
13
|
-
}
|
|
14
|
-
async initialize() {
|
|
15
|
-
const deviceResponse = await getAudioDevices();
|
|
16
|
-
this.devices = deviceResponse.devices;
|
|
17
|
-
this.selectedDevice = deviceResponse.defaultDevice;
|
|
18
|
-
return { devices: this.devices, selectedDevice: this.selectedDevice };
|
|
19
|
-
}
|
|
20
|
-
dispatchCustomEvent(eventName, detail) {
|
|
21
|
-
this.dispatchEvent(new CustomEvent(eventName, {
|
|
22
|
-
detail,
|
|
23
|
-
bubbles: true,
|
|
24
|
-
composed: true,
|
|
25
|
-
}));
|
|
26
|
-
}
|
|
27
|
-
async handleDevicesChange() {
|
|
28
|
-
const deviceResponse = await getAudioDevices();
|
|
29
|
-
this.devices = deviceResponse.devices;
|
|
30
|
-
if (!this.devices.find(device => device.deviceId === this.selectedDevice?.deviceId)) {
|
|
31
|
-
this.selectedDevice = deviceResponse.defaultDevice;
|
|
32
|
-
}
|
|
33
|
-
this.dispatchCustomEvent('recording-devices-changed', {
|
|
34
|
-
devices: deviceResponse.devices,
|
|
35
|
-
selectedDevice: this.selectedDevice || deviceResponse.defaultDevice,
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
async startRecording(params) {
|
|
39
|
-
this._updateRecordingState('initializing');
|
|
40
|
-
try {
|
|
41
|
-
this._mediaStream = await getMediaStream(params.debug_displayAudio
|
|
42
|
-
? 'display_audio'
|
|
43
|
-
: this.selectedDevice?.deviceId);
|
|
44
|
-
this._mediaStream.getTracks().forEach((track) => {
|
|
45
|
-
track.addEventListener('ended', () => {
|
|
46
|
-
if (this.recordingState === 'recording') {
|
|
47
|
-
this.dispatchCustomEvent('error', {
|
|
48
|
-
message: 'Microphone access was lost.',
|
|
49
|
-
});
|
|
50
|
-
this.stopRecording();
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
this._audioService = new AudioService(this._mediaStream);
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
this.dispatchCustomEvent('error', error);
|
|
58
|
-
this.stopRecording();
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
// Initialize dictation service.
|
|
62
|
-
try {
|
|
63
|
-
this._dictationService = new DictationService(this._mediaStream, params);
|
|
64
|
-
// Forward custom events from dictation service
|
|
65
|
-
this._dictationService.addEventListener('error', e => {
|
|
66
|
-
this.dispatchEvent(new CustomEvent('error', {
|
|
67
|
-
detail: e.detail,
|
|
68
|
-
bubbles: true,
|
|
69
|
-
composed: true,
|
|
70
|
-
}));
|
|
71
|
-
this.stopRecording();
|
|
72
|
-
});
|
|
73
|
-
this._dictationService.addEventListener('stream-closed', (e) => {
|
|
74
|
-
this.dispatchEvent(new CustomEvent('stream-closed', {
|
|
75
|
-
detail: e.detail,
|
|
76
|
-
bubbles: true,
|
|
77
|
-
composed: true,
|
|
78
|
-
}));
|
|
79
|
-
this.stopRecording();
|
|
80
|
-
});
|
|
81
|
-
this._dictationService.addEventListener('transcript', e => this.dispatchEvent(new CustomEvent('transcript', {
|
|
82
|
-
detail: e.detail,
|
|
83
|
-
bubbles: true,
|
|
84
|
-
composed: true,
|
|
85
|
-
})));
|
|
86
|
-
this._dictationService.addEventListener('command', e => this.dispatchEvent(new CustomEvent('command', {
|
|
87
|
-
detail: e.detail,
|
|
88
|
-
bubbles: true,
|
|
89
|
-
composed: true,
|
|
90
|
-
})));
|
|
91
|
-
this._dictationService.addEventListener('usage', e => this.dispatchEvent(new CustomEvent('usage', {
|
|
92
|
-
detail: e.detail,
|
|
93
|
-
bubbles: true,
|
|
94
|
-
composed: true,
|
|
95
|
-
})));
|
|
96
|
-
}
|
|
97
|
-
catch (error) {
|
|
98
|
-
this.dispatchCustomEvent('error', error);
|
|
99
|
-
this.stopRecording();
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
try {
|
|
103
|
-
this._dictationService?.startRecording();
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
this.dispatchEvent(new CustomEvent('error', {
|
|
107
|
-
detail: error,
|
|
108
|
-
bubbles: true,
|
|
109
|
-
composed: true,
|
|
110
|
-
}));
|
|
111
|
-
this.stopRecording();
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
this._updateRecordingState('recording');
|
|
115
|
-
// Update audio level visualization.
|
|
116
|
-
this._visualiserInterval = window.setInterval(() => {
|
|
117
|
-
const level = this._audioService
|
|
118
|
-
? this._audioService.getAudioLevel() * 3
|
|
119
|
-
: 0;
|
|
120
|
-
this.dispatchCustomEvent('audio-level-changed', { audioLevel: level });
|
|
121
|
-
}, 150);
|
|
122
|
-
}
|
|
123
|
-
async stopRecording() {
|
|
124
|
-
this._updateRecordingState('stopping');
|
|
125
|
-
if (this._visualiserInterval) {
|
|
126
|
-
clearInterval(this._visualiserInterval);
|
|
127
|
-
this._visualiserInterval = undefined;
|
|
128
|
-
}
|
|
129
|
-
if (this._mediaStream) {
|
|
130
|
-
this._mediaStream.getTracks().forEach(track => track.stop());
|
|
131
|
-
}
|
|
132
|
-
this.dispatchCustomEvent('audio-level-changed', { audioLevel: 0 });
|
|
133
|
-
await this._dictationService?.stopRecording();
|
|
134
|
-
this._updateRecordingState('stopped');
|
|
135
|
-
}
|
|
136
|
-
_updateRecordingState(state) {
|
|
137
|
-
this.recordingState = state;
|
|
138
|
-
this.dispatchEvent(new CustomEvent('recording-state-changed', {
|
|
139
|
-
detail: { state },
|
|
140
|
-
bubbles: true,
|
|
141
|
-
composed: true,
|
|
142
|
-
}));
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
//# sourceMappingURL=RecorderManager.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RecorderManager.js","sourceRoot":"","sources":["../src/RecorderManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { Corti } from '@corti/sdk';\n\nimport { getAudioDevices, getMediaStream } from './utils.js';\nimport { AudioService } from './audioService.js';\nimport { DictationService } from './DictationService.js';\nimport type { RecordingState, ServerConfig } from './types.js';\n\nexport class RecorderManager extends EventTarget {\n public devices: MediaDeviceInfo[] = [];\n\n public selectedDevice: MediaDeviceInfo | undefined;\n\n public recordingState: RecordingState = 'stopped';\n\n private _mediaStream: MediaStream | null = null;\n\n private _audioService: AudioService | null = null;\n\n private _dictationService: DictationService | null = null;\n\n private _visualiserInterval?: number;\n\n constructor() {\n super();\n navigator.mediaDevices.addEventListener(\n 'devicechange',\n this.handleDevicesChange.bind(this),\n );\n }\n\n async initialize() {\n const deviceResponse = await getAudioDevices();\n this.devices = deviceResponse.devices;\n this.selectedDevice = deviceResponse.defaultDevice;\n return { devices: this.devices, selectedDevice: this.selectedDevice };\n }\n\n private dispatchCustomEvent(eventName: string, detail?: unknown): void {\n this.dispatchEvent(\n new CustomEvent(eventName, {\n detail,\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private async handleDevicesChange() {\n const deviceResponse = await getAudioDevices();\n this.devices = deviceResponse.devices;\n if (\n !this.devices.find(\n device => device.deviceId === this.selectedDevice?.deviceId,\n )\n ) {\n this.selectedDevice = deviceResponse.defaultDevice;\n }\n this.dispatchCustomEvent('recording-devices-changed', {\n devices: deviceResponse.devices,\n selectedDevice: this.selectedDevice || deviceResponse.defaultDevice,\n });\n }\n\n async startRecording(params: {\n dictationConfig: Corti.TranscribeConfig;\n serverConfig: ServerConfig;\n debug_displayAudio?: boolean;\n }): Promise<void> {\n this._updateRecordingState('initializing');\n\n try {\n this._mediaStream = await getMediaStream(\n params.debug_displayAudio\n ? 'display_audio'\n : this.selectedDevice?.deviceId,\n );\n\n this._mediaStream.getTracks().forEach((track: MediaStreamTrack) => {\n track.addEventListener('ended', () => {\n if (this.recordingState === 'recording') {\n this.dispatchCustomEvent('error', {\n message: 'Microphone access was lost.',\n });\n this.stopRecording();\n }\n });\n });\n\n this._audioService = new AudioService(this._mediaStream);\n } catch (error) {\n this.dispatchCustomEvent('error', error);\n this.stopRecording();\n return;\n }\n\n // Initialize dictation service.\n try {\n this._dictationService = new DictationService(this._mediaStream, params);\n\n // Forward custom events from dictation service\n this._dictationService.addEventListener('error', e => {\n this.dispatchEvent(\n new CustomEvent('error', {\n detail: (e as CustomEvent).detail,\n bubbles: true,\n composed: true,\n }),\n );\n this.stopRecording();\n });\n this._dictationService.addEventListener('stream-closed', (e) => {\n this.dispatchEvent(\n new CustomEvent('stream-closed', {\n detail: (e as CustomEvent).detail,\n bubbles: true,\n composed: true,\n }),\n );\n this.stopRecording();\n });\n this._dictationService.addEventListener('transcript', e =>\n this.dispatchEvent(\n new CustomEvent('transcript', {\n detail: (e as CustomEvent).detail,\n bubbles: true,\n composed: true,\n }),\n ),\n );\n this._dictationService.addEventListener('command', e =>\n this.dispatchEvent(\n new CustomEvent('command', {\n detail: (e as CustomEvent).detail,\n bubbles: true,\n composed: true,\n }),\n ),\n );\n this._dictationService.addEventListener('usage', e =>\n this.dispatchEvent(\n new CustomEvent('usage', {\n detail: (e as CustomEvent).detail,\n bubbles: true,\n composed: true,\n }),\n ),\n );\n } catch (error) {\n this.dispatchCustomEvent('error', error);\n this.stopRecording();\n return;\n }\n\n try {\n this._dictationService?.startRecording();\n } catch (error) {\n this.dispatchEvent(\n new CustomEvent('error', {\n detail: error,\n bubbles: true,\n composed: true,\n }),\n );\n this.stopRecording();\n return;\n }\n this._updateRecordingState('recording');\n\n // Update audio level visualization.\n this._visualiserInterval = window.setInterval(() => {\n const level = this._audioService\n ? this._audioService.getAudioLevel() * 3\n : 0;\n this.dispatchCustomEvent('audio-level-changed', { audioLevel: level });\n }, 150);\n }\n\n async stopRecording() {\n this._updateRecordingState('stopping');\n if (this._visualiserInterval) {\n clearInterval(this._visualiserInterval);\n this._visualiserInterval = undefined;\n }\n if (this._mediaStream) {\n this._mediaStream.getTracks().forEach(track => track.stop());\n }\n this.dispatchCustomEvent('audio-level-changed', { audioLevel: 0 });\n await this._dictationService?.stopRecording();\n this._updateRecordingState('stopped');\n }\n\n private _updateRecordingState(state: RecordingState) {\n this.recordingState = state;\n this.dispatchEvent(\n new CustomEvent('recording-state-changed', {\n detail: { state },\n bubbles: true,\n composed: true,\n }),\n );\n }\n}\n"]}
|
package/dist/audioService.d.ts
DELETED
package/dist/audioService.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export class AudioService {
|
|
2
|
-
constructor(mediaStream) {
|
|
3
|
-
this.audioContext = new AudioContext();
|
|
4
|
-
const source = this.audioContext.createMediaStreamSource(mediaStream);
|
|
5
|
-
this.analyser = this.audioContext.createAnalyser();
|
|
6
|
-
this.analyser.fftSize = 8192;
|
|
7
|
-
source.connect(this.analyser);
|
|
8
|
-
}
|
|
9
|
-
getAudioLevel() {
|
|
10
|
-
const bufferLength = this.analyser.fftSize;
|
|
11
|
-
const dataArray = new Uint8Array(bufferLength);
|
|
12
|
-
this.analyser.getByteTimeDomainData(dataArray);
|
|
13
|
-
let sum = 0;
|
|
14
|
-
for (let i = 0; i < bufferLength; i += 1) {
|
|
15
|
-
const normalized = (dataArray[i] - 128) / 128;
|
|
16
|
-
sum += normalized * normalized;
|
|
17
|
-
}
|
|
18
|
-
return Math.sqrt(sum / bufferLength);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
//# sourceMappingURL=audioService.js.map
|
package/dist/audioService.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"audioService.js","sourceRoot":"","sources":["../src/audioService.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,YAAY;IAKvB,YAAY,WAAwB;QAClC,IAAI,CAAC,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,uBAAuB,CAAC,WAAW,CAAC,CAAC;QACtE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,CAAC;QACnD,IAAI,CAAC,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;QAC7B,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAEM,aAAa;QAClB,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;QAC3C,MAAM,SAAS,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC;QAC/C,IAAI,CAAC,QAAQ,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,GAAG,GAAG,CAAC,CAAC;QACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,MAAM,UAAU,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;YAC9C,GAAG,IAAI,UAAU,GAAG,UAAU,CAAC;QACjC,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,YAAY,CAAC,CAAC;IACvC,CAAC;CACF","sourcesContent":["export class AudioService {\n private audioContext: AudioContext;\n\n private analyser: AnalyserNode;\n\n constructor(mediaStream: MediaStream) {\n this.audioContext = new AudioContext();\n const source = this.audioContext.createMediaStreamSource(mediaStream);\n this.analyser = this.audioContext.createAnalyser();\n this.analyser.fftSize = 8192;\n source.connect(this.analyser);\n }\n\n public getAudioLevel(): number {\n const bufferLength = this.analyser.fftSize;\n const dataArray = new Uint8Array(bufferLength);\n this.analyser.getByteTimeDomainData(dataArray);\n let sum = 0;\n for (let i = 0; i < bufferLength; i += 1) {\n const normalized = (dataArray[i] - 128) / 128;\n sum += normalized * normalized;\n }\n return Math.sqrt(sum / bufferLength);\n }\n}\n"]}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { type Corti } from "@corti/sdk";
|
|
2
|
-
import type { ReactiveController, ReactiveControllerHost } from "lit";
|
|
3
|
-
import type { ProxyOptions } from "../types.js";
|
|
4
|
-
interface DictationControllerHost extends ReactiveControllerHost {
|
|
5
|
-
_accessToken?: string;
|
|
6
|
-
_authConfig?: Corti.BearerOptions;
|
|
7
|
-
_region?: string;
|
|
8
|
-
_tenantName?: string;
|
|
9
|
-
_socketUrl?: string;
|
|
10
|
-
_socketProxy?: ProxyOptions;
|
|
11
|
-
}
|
|
12
|
-
export type TranscribeMessage = Corti.TranscribeConfigStatusMessage | Corti.TranscribeUsageMessage | Corti.TranscribeEndedMessage | Corti.TranscribeErrorMessage | Corti.TranscribeTranscriptMessage | Corti.TranscribeCommandMessage | Corti.TranscribeFlushedMessage;
|
|
13
|
-
interface WebSocketCallbacks {
|
|
14
|
-
onMessage?: (message: TranscribeMessage) => void;
|
|
15
|
-
onError?: (error: Error) => void;
|
|
16
|
-
onClose?: (event: unknown) => void;
|
|
17
|
-
onNetworkActivity?: (direction: "sent" | "received", data: unknown) => void;
|
|
18
|
-
}
|
|
19
|
-
export declare class DictationController implements ReactiveController {
|
|
20
|
-
host: DictationControllerHost;
|
|
21
|
-
private _cortiClient;
|
|
22
|
-
private _webSocket;
|
|
23
|
-
private _closeTimeout?;
|
|
24
|
-
private _onNetworkActivity?;
|
|
25
|
-
constructor(host: DictationControllerHost);
|
|
26
|
-
hostDisconnected(): void;
|
|
27
|
-
connect(mediaRecorder: MediaRecorder | null, dictationConfig?: Corti.TranscribeConfig, callbacks?: WebSocketCallbacks): Promise<void>;
|
|
28
|
-
private connectProxy;
|
|
29
|
-
private connectAuth;
|
|
30
|
-
private setupWebSocketHandlers;
|
|
31
|
-
private setupMediaRecorder;
|
|
32
|
-
disconnect(onClose?: (event: unknown) => void): Promise<void>;
|
|
33
|
-
cleanup(): void;
|
|
34
|
-
}
|
|
35
|
-
export {};
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { CortiClient, CortiWebSocketProxyClient } from "@corti/sdk";
|
|
2
|
-
import { DEFAULT_DICTATION_CONFIG } from "../constants.js";
|
|
3
|
-
export class DictationController {
|
|
4
|
-
constructor(host) {
|
|
5
|
-
this._cortiClient = null;
|
|
6
|
-
this._webSocket = null;
|
|
7
|
-
this.host = host;
|
|
8
|
-
host.addController(this);
|
|
9
|
-
}
|
|
10
|
-
hostDisconnected() {
|
|
11
|
-
this.cleanup();
|
|
12
|
-
}
|
|
13
|
-
async connect(mediaRecorder, dictationConfig = DEFAULT_DICTATION_CONFIG, callbacks = {}) {
|
|
14
|
-
if (!mediaRecorder) {
|
|
15
|
-
throw new Error("MediaRecorder is required to connect");
|
|
16
|
-
}
|
|
17
|
-
if (this._webSocket?.readyState === WebSocket.OPEN) {
|
|
18
|
-
throw new Error("Already connected. Disconnect before reconnecting.");
|
|
19
|
-
}
|
|
20
|
-
this._webSocket =
|
|
21
|
-
this.host._socketUrl || this.host._socketProxy
|
|
22
|
-
? await this.connectProxy(dictationConfig)
|
|
23
|
-
: await this.connectAuth(dictationConfig);
|
|
24
|
-
this._onNetworkActivity = callbacks.onNetworkActivity;
|
|
25
|
-
this.setupMediaRecorder(mediaRecorder);
|
|
26
|
-
this.setupWebSocketHandlers(callbacks);
|
|
27
|
-
}
|
|
28
|
-
async connectProxy(dictationConfig) {
|
|
29
|
-
const proxyOptions = this.host._socketProxy || {
|
|
30
|
-
url: this.host._socketUrl || "",
|
|
31
|
-
};
|
|
32
|
-
if (!proxyOptions.url) {
|
|
33
|
-
throw new Error("Proxy URL is required when using proxy client");
|
|
34
|
-
}
|
|
35
|
-
return await CortiWebSocketProxyClient.transcribe.connect({
|
|
36
|
-
configuration: dictationConfig,
|
|
37
|
-
proxy: proxyOptions,
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
async connectAuth(dictationConfig) {
|
|
41
|
-
if (!this.host._authConfig && !this.host._accessToken) {
|
|
42
|
-
throw new Error("Auth configuration or access token is required to connect");
|
|
43
|
-
}
|
|
44
|
-
// Use authConfig if available, otherwise create one from accessToken
|
|
45
|
-
const auth = this.host._authConfig || {
|
|
46
|
-
accessToken: this.host._accessToken || "",
|
|
47
|
-
refreshAccessToken: () => ({
|
|
48
|
-
accessToken: this.host._accessToken || "",
|
|
49
|
-
}),
|
|
50
|
-
};
|
|
51
|
-
this._cortiClient = new CortiClient({
|
|
52
|
-
auth,
|
|
53
|
-
environment: this.host._region,
|
|
54
|
-
tenantName: this.host._tenantName,
|
|
55
|
-
});
|
|
56
|
-
return await this._cortiClient.transcribe.connect({
|
|
57
|
-
configuration: dictationConfig,
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
setupWebSocketHandlers(callbacks) {
|
|
61
|
-
if (!this._webSocket) {
|
|
62
|
-
throw new Error("WebSocket not initialized");
|
|
63
|
-
}
|
|
64
|
-
this._webSocket.on("message", (message) => {
|
|
65
|
-
this._onNetworkActivity?.("received", message);
|
|
66
|
-
if (callbacks.onMessage) {
|
|
67
|
-
callbacks.onMessage(message);
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
this._webSocket.on("error", (event) => {
|
|
71
|
-
if (callbacks.onError) {
|
|
72
|
-
callbacks.onError(event);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
this._webSocket.on("close", (event) => {
|
|
76
|
-
if (callbacks.onClose) {
|
|
77
|
-
callbacks.onClose(event);
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
setupMediaRecorder(mediaRecorder) {
|
|
82
|
-
mediaRecorder.ondataavailable = (event) => {
|
|
83
|
-
this._webSocket?.sendAudio(event.data);
|
|
84
|
-
this._onNetworkActivity?.("sent", {
|
|
85
|
-
size: event.data.size,
|
|
86
|
-
type: "audio",
|
|
87
|
-
});
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
async disconnect(onClose) {
|
|
91
|
-
await new Promise((resolve, reject) => {
|
|
92
|
-
if (!this._webSocket || this._webSocket.readyState !== WebSocket.OPEN) {
|
|
93
|
-
resolve();
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
this._webSocket.on("close", (event) => {
|
|
97
|
-
if (this._closeTimeout) {
|
|
98
|
-
clearTimeout(this._closeTimeout);
|
|
99
|
-
this._closeTimeout = undefined;
|
|
100
|
-
}
|
|
101
|
-
if (onClose) {
|
|
102
|
-
onClose(event);
|
|
103
|
-
}
|
|
104
|
-
resolve();
|
|
105
|
-
});
|
|
106
|
-
this._webSocket.sendEnd({ type: "end" });
|
|
107
|
-
this._onNetworkActivity?.("sent", { type: "end" });
|
|
108
|
-
this._closeTimeout = window.setTimeout(() => {
|
|
109
|
-
// Reject the promise before closing the web socket, so the promise rejects before close event fires
|
|
110
|
-
reject(new Error("WebSocket close timeout"));
|
|
111
|
-
if (this._webSocket?.readyState === WebSocket.OPEN) {
|
|
112
|
-
this._webSocket.close();
|
|
113
|
-
}
|
|
114
|
-
}, 10000);
|
|
115
|
-
});
|
|
116
|
-
this.cleanup();
|
|
117
|
-
}
|
|
118
|
-
cleanup() {
|
|
119
|
-
if (this._closeTimeout) {
|
|
120
|
-
clearTimeout(this._closeTimeout);
|
|
121
|
-
this._closeTimeout = undefined;
|
|
122
|
-
}
|
|
123
|
-
if (this._webSocket?.readyState === WebSocket.OPEN) {
|
|
124
|
-
this._webSocket.close();
|
|
125
|
-
}
|
|
126
|
-
this._webSocket = null;
|
|
127
|
-
this._cortiClient = null;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
//# sourceMappingURL=DictationController.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DictationController.js","sourceRoot":"","sources":["../../src/controllers/DictationController.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Corti, CortiClient, CortiWebSocketProxyClient } from \"@corti/sdk\";\nimport type { ReactiveController, ReactiveControllerHost } from \"lit\";\nimport { DEFAULT_DICTATION_CONFIG } from \"../constants.js\";\nimport type { ProxyOptions } from \"../types.js\";\n\ntype TranscribeSocket = Awaited<\n ReturnType<CortiClient[\"transcribe\"][\"connect\"]>\n>;\n\ninterface DictationControllerHost extends ReactiveControllerHost {\n _accessToken?: string;\n _authConfig?: Corti.BearerOptions;\n _region?: string;\n _tenantName?: string;\n _socketUrl?: string;\n _socketProxy?: ProxyOptions;\n}\n\nexport type TranscribeMessage =\n | Corti.TranscribeConfigStatusMessage\n | Corti.TranscribeUsageMessage\n | Corti.TranscribeEndedMessage\n | Corti.TranscribeErrorMessage\n | Corti.TranscribeTranscriptMessage\n | Corti.TranscribeCommandMessage\n | Corti.TranscribeFlushedMessage;\n\ninterface WebSocketCallbacks {\n onMessage?: (message: TranscribeMessage) => void;\n onError?: (error: Error) => void;\n onClose?: (event: unknown) => void;\n onNetworkActivity?: (direction: \"sent\" | \"received\", data: unknown) => void;\n}\n\nexport class DictationController implements ReactiveController {\n host: DictationControllerHost;\n\n private _cortiClient: CortiClient | null = null;\n private _webSocket: TranscribeSocket | null = null;\n private _closeTimeout?: number;\n private _onNetworkActivity?: WebSocketCallbacks[\"onNetworkActivity\"];\n\n constructor(host: DictationControllerHost) {\n this.host = host;\n host.addController(this);\n }\n\n hostDisconnected(): void {\n this.cleanup();\n }\n\n async connect(\n mediaRecorder: MediaRecorder | null,\n dictationConfig: Corti.TranscribeConfig = DEFAULT_DICTATION_CONFIG,\n callbacks: WebSocketCallbacks = {},\n ): Promise<void> {\n if (!mediaRecorder) {\n throw new Error(\"MediaRecorder is required to connect\");\n }\n\n if (this._webSocket?.readyState === WebSocket.OPEN) {\n throw new Error(\"Already connected. Disconnect before reconnecting.\");\n }\n\n this._webSocket =\n this.host._socketUrl || this.host._socketProxy\n ? await this.connectProxy(dictationConfig)\n : await this.connectAuth(dictationConfig);\n\n this._onNetworkActivity = callbacks.onNetworkActivity;\n this.setupMediaRecorder(mediaRecorder);\n this.setupWebSocketHandlers(callbacks);\n }\n\n private async connectProxy(\n dictationConfig: Corti.TranscribeConfig,\n ): Promise<TranscribeSocket> {\n const proxyOptions = this.host._socketProxy || {\n url: this.host._socketUrl || \"\",\n };\n\n if (!proxyOptions.url) {\n throw new Error(\"Proxy URL is required when using proxy client\");\n }\n\n return await CortiWebSocketProxyClient.transcribe.connect({\n configuration: dictationConfig,\n proxy: proxyOptions,\n });\n }\n\n private async connectAuth(\n dictationConfig: Corti.TranscribeConfig,\n ): Promise<TranscribeSocket> {\n if (!this.host._authConfig && !this.host._accessToken) {\n throw new Error(\n \"Auth configuration or access token is required to connect\",\n );\n }\n\n // Use authConfig if available, otherwise create one from accessToken\n const auth: Corti.BearerOptions = this.host._authConfig || {\n accessToken: this.host._accessToken || \"\",\n refreshAccessToken: () => ({\n accessToken: this.host._accessToken || \"\",\n }),\n };\n\n this._cortiClient = new CortiClient({\n auth,\n environment: this.host._region,\n tenantName: this.host._tenantName,\n });\n\n return await this._cortiClient.transcribe.connect({\n configuration: dictationConfig,\n });\n }\n\n private setupWebSocketHandlers(callbacks: WebSocketCallbacks): void {\n if (!this._webSocket) {\n throw new Error(\"WebSocket not initialized\");\n }\n\n this._webSocket.on(\"message\", (message: TranscribeMessage) => {\n this._onNetworkActivity?.(\"received\", message);\n\n if (callbacks.onMessage) {\n callbacks.onMessage(message);\n }\n });\n\n this._webSocket.on(\"error\", (event: Error) => {\n if (callbacks.onError) {\n callbacks.onError(event);\n }\n });\n\n this._webSocket.on(\"close\", (event: unknown) => {\n if (callbacks.onClose) {\n callbacks.onClose(event);\n }\n });\n }\n\n private setupMediaRecorder(mediaRecorder: MediaRecorder): void {\n mediaRecorder.ondataavailable = (event) => {\n this._webSocket?.sendAudio(event.data);\n this._onNetworkActivity?.(\"sent\", {\n size: event.data.size,\n type: \"audio\",\n });\n };\n }\n\n async disconnect(onClose?: (event: unknown) => void): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n if (!this._webSocket || this._webSocket.readyState !== WebSocket.OPEN) {\n resolve();\n return;\n }\n\n this._webSocket.on(\"close\", (event) => {\n if (this._closeTimeout) {\n clearTimeout(this._closeTimeout);\n this._closeTimeout = undefined;\n }\n\n if (onClose) {\n onClose(event);\n }\n\n resolve();\n });\n\n this._webSocket.sendEnd({ type: \"end\" });\n this._onNetworkActivity?.(\"sent\", { type: \"end\" });\n\n this._closeTimeout = window.setTimeout(() => {\n // Reject the promise before closing the web socket, so the promise rejects before close event fires\n reject(new Error(\"WebSocket close timeout\"));\n\n if (this._webSocket?.readyState === WebSocket.OPEN) {\n this._webSocket.close();\n }\n }, 10000);\n });\n\n this.cleanup();\n }\n\n cleanup(): void {\n if (this._closeTimeout) {\n clearTimeout(this._closeTimeout);\n this._closeTimeout = undefined;\n }\n\n if (this._webSocket?.readyState === WebSocket.OPEN) {\n this._webSocket.close();\n }\n\n this._webSocket = null;\n this._cortiClient = null;\n }\n}\n"]}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { ReactiveController, ReactiveControllerHost } from "lit";
|
|
2
|
-
interface MediaControllerHost extends ReactiveControllerHost {
|
|
3
|
-
_selectedDevice?: MediaDeviceInfo;
|
|
4
|
-
_debug_displayAudio?: boolean;
|
|
5
|
-
}
|
|
6
|
-
export declare class MediaController implements ReactiveController {
|
|
7
|
-
host: MediaControllerHost;
|
|
8
|
-
private _mediaStream;
|
|
9
|
-
private _audioContext;
|
|
10
|
-
private _analyser;
|
|
11
|
-
private _mediaRecorder;
|
|
12
|
-
private _visualiserInterval?;
|
|
13
|
-
private _audioLevel;
|
|
14
|
-
private _onTrackEnded?;
|
|
15
|
-
private _onAudioLevelChange?;
|
|
16
|
-
constructor(host: MediaControllerHost);
|
|
17
|
-
hostDisconnected(): void;
|
|
18
|
-
initialize(onTrackEnded?: () => void): Promise<void>;
|
|
19
|
-
getAudioLevel(): number;
|
|
20
|
-
startAudioLevelMonitoring(onAudioLevelChange?: (level: number) => void): void;
|
|
21
|
-
stopAudioLevelMonitoring(): void;
|
|
22
|
-
cleanup(): Promise<void>;
|
|
23
|
-
/**
|
|
24
|
-
* Stops the media recorder and waits for all buffered data to be flushed.
|
|
25
|
-
* This ensures the final ondataavailable event fires before resolving.
|
|
26
|
-
*/
|
|
27
|
-
stopRecording(): Promise<void>;
|
|
28
|
-
get mediaRecorder(): MediaRecorder | null;
|
|
29
|
-
get audioLevel(): number;
|
|
30
|
-
}
|
|
31
|
-
export {};
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { calculateAudioLevel, createAudioAnalyzer, getMediaStream, } from "../utils/media.js";
|
|
2
|
-
export class MediaController {
|
|
3
|
-
constructor(host) {
|
|
4
|
-
this._mediaStream = null;
|
|
5
|
-
this._audioContext = null;
|
|
6
|
-
this._analyser = null;
|
|
7
|
-
this._mediaRecorder = null;
|
|
8
|
-
this._audioLevel = 0;
|
|
9
|
-
this.host = host;
|
|
10
|
-
host.addController(this);
|
|
11
|
-
}
|
|
12
|
-
hostDisconnected() {
|
|
13
|
-
this.cleanup();
|
|
14
|
-
}
|
|
15
|
-
async initialize(onTrackEnded) {
|
|
16
|
-
await this.cleanup();
|
|
17
|
-
this._onTrackEnded = onTrackEnded;
|
|
18
|
-
this._mediaStream = await getMediaStream(this.host._selectedDevice?.deviceId, this.host._debug_displayAudio);
|
|
19
|
-
this._mediaStream.getTracks().forEach((track) => {
|
|
20
|
-
track.addEventListener("ended", () => {
|
|
21
|
-
if (this._onTrackEnded) {
|
|
22
|
-
this._onTrackEnded();
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
const { audioContext, analyser } = createAudioAnalyzer(this._mediaStream);
|
|
27
|
-
this._audioContext = audioContext;
|
|
28
|
-
this._analyser = analyser;
|
|
29
|
-
this._mediaRecorder = new MediaRecorder(this._mediaStream);
|
|
30
|
-
}
|
|
31
|
-
getAudioLevel() {
|
|
32
|
-
return this._analyser ? calculateAudioLevel(this._analyser) : 0;
|
|
33
|
-
}
|
|
34
|
-
startAudioLevelMonitoring(onAudioLevelChange) {
|
|
35
|
-
this.stopAudioLevelMonitoring();
|
|
36
|
-
this._onAudioLevelChange = onAudioLevelChange;
|
|
37
|
-
this._visualiserInterval = window.setInterval(() => {
|
|
38
|
-
this._audioLevel = this.getAudioLevel() * 3;
|
|
39
|
-
this.host.requestUpdate();
|
|
40
|
-
if (this._onAudioLevelChange) {
|
|
41
|
-
this._onAudioLevelChange(this._audioLevel);
|
|
42
|
-
}
|
|
43
|
-
}, 150);
|
|
44
|
-
}
|
|
45
|
-
stopAudioLevelMonitoring() {
|
|
46
|
-
if (this._visualiserInterval) {
|
|
47
|
-
clearInterval(this._visualiserInterval);
|
|
48
|
-
this._visualiserInterval = undefined;
|
|
49
|
-
}
|
|
50
|
-
this._audioLevel = 0;
|
|
51
|
-
this.host.requestUpdate();
|
|
52
|
-
if (this._onAudioLevelChange) {
|
|
53
|
-
this._onAudioLevelChange(this._audioLevel);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
async cleanup() {
|
|
57
|
-
this.stopAudioLevelMonitoring();
|
|
58
|
-
if (this._mediaRecorder?.state === "recording") {
|
|
59
|
-
this._mediaRecorder.stop();
|
|
60
|
-
}
|
|
61
|
-
if (this._mediaStream) {
|
|
62
|
-
this._mediaStream.getTracks().forEach((track) => {
|
|
63
|
-
track.stop();
|
|
64
|
-
});
|
|
65
|
-
this._mediaStream = null;
|
|
66
|
-
}
|
|
67
|
-
if (this._audioContext && this._audioContext.state !== "closed") {
|
|
68
|
-
await this._audioContext.close();
|
|
69
|
-
}
|
|
70
|
-
this._audioContext = null;
|
|
71
|
-
this._analyser = null;
|
|
72
|
-
this._mediaRecorder = null;
|
|
73
|
-
this._onTrackEnded = undefined;
|
|
74
|
-
this._onAudioLevelChange = undefined;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Stops the media recorder and waits for all buffered data to be flushed.
|
|
78
|
-
* This ensures the final ondataavailable event fires before resolving.
|
|
79
|
-
*/
|
|
80
|
-
async stopRecording() {
|
|
81
|
-
return new Promise((resolve) => {
|
|
82
|
-
if (!this._mediaRecorder || this._mediaRecorder.state !== "recording") {
|
|
83
|
-
resolve();
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
this._mediaRecorder.onstop = () => {
|
|
87
|
-
resolve();
|
|
88
|
-
};
|
|
89
|
-
this._mediaRecorder.stop();
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
get mediaRecorder() {
|
|
93
|
-
return this._mediaRecorder;
|
|
94
|
-
}
|
|
95
|
-
get audioLevel() {
|
|
96
|
-
return this._audioLevel;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
//# sourceMappingURL=MediaController.js.map
|