react-native-voice-ts 1.0.3 → 1.0.5
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.
|
@@ -32,7 +32,7 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
|
|
|
32
32
|
const [recognizedText, setRecognizedText] = useState('');
|
|
33
33
|
const [partialText, setPartialText] = useState('');
|
|
34
34
|
const [error, setError] = useState(null);
|
|
35
|
-
const
|
|
35
|
+
const shouldContinueRef = React.useRef(false);
|
|
36
36
|
const silenceTimerRef = React.useRef(null);
|
|
37
37
|
useEffect(() => {
|
|
38
38
|
// Clear any existing timers on cleanup
|
|
@@ -55,10 +55,10 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
|
|
|
55
55
|
Voice.onSpeechEnd = async () => {
|
|
56
56
|
setIsRecording(false);
|
|
57
57
|
// In continuous mode, restart listening after results
|
|
58
|
-
if (continuous &&
|
|
58
|
+
if (continuous && shouldContinueRef.current) {
|
|
59
59
|
// Small delay before restarting
|
|
60
60
|
setTimeout(async () => {
|
|
61
|
-
if (
|
|
61
|
+
if (shouldContinueRef.current) {
|
|
62
62
|
try {
|
|
63
63
|
await Voice.start(locale, {
|
|
64
64
|
EXTRA_PARTIAL_RESULTS: enablePartialResults,
|
|
@@ -78,7 +78,7 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
|
|
|
78
78
|
const errorMessage = e.error?.message || 'Unknown error';
|
|
79
79
|
setError(errorMessage);
|
|
80
80
|
setIsRecording(false);
|
|
81
|
-
|
|
81
|
+
shouldContinueRef.current = false;
|
|
82
82
|
if (silenceTimerRef.current) {
|
|
83
83
|
clearTimeout(silenceTimerRef.current);
|
|
84
84
|
}
|
|
@@ -109,9 +109,15 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
|
|
|
109
109
|
// Reset silence timer on partial results (user is speaking)
|
|
110
110
|
if (continuous && silenceTimerRef.current) {
|
|
111
111
|
clearTimeout(silenceTimerRef.current);
|
|
112
|
-
silenceTimerRef.current = setTimeout(() => {
|
|
113
|
-
if (
|
|
114
|
-
|
|
112
|
+
silenceTimerRef.current = setTimeout(async () => {
|
|
113
|
+
if (shouldContinueRef.current) {
|
|
114
|
+
shouldContinueRef.current = false;
|
|
115
|
+
if (silenceTimerRef.current) {
|
|
116
|
+
clearTimeout(silenceTimerRef.current);
|
|
117
|
+
silenceTimerRef.current = null;
|
|
118
|
+
}
|
|
119
|
+
await Voice.stop();
|
|
120
|
+
onStop?.();
|
|
115
121
|
}
|
|
116
122
|
}, maxSilenceDuration);
|
|
117
123
|
}
|
|
@@ -130,7 +136,6 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
|
|
|
130
136
|
onError,
|
|
131
137
|
enablePartialResults,
|
|
132
138
|
continuous,
|
|
133
|
-
shouldContinue,
|
|
134
139
|
recognizedText,
|
|
135
140
|
locale,
|
|
136
141
|
maxSilenceDuration,
|
|
@@ -149,7 +154,7 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
|
|
|
149
154
|
setRecognizedText('');
|
|
150
155
|
setPartialText('');
|
|
151
156
|
}
|
|
152
|
-
|
|
157
|
+
shouldContinueRef.current = true;
|
|
153
158
|
// Check permission (Android only)
|
|
154
159
|
const hasPermission = await Voice.checkMicrophonePermission();
|
|
155
160
|
if (!hasPermission) {
|
|
@@ -164,9 +169,15 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
|
|
|
164
169
|
});
|
|
165
170
|
// Start silence timer if in continuous mode
|
|
166
171
|
if (continuous) {
|
|
167
|
-
silenceTimerRef.current = setTimeout(() => {
|
|
168
|
-
if (
|
|
169
|
-
|
|
172
|
+
silenceTimerRef.current = setTimeout(async () => {
|
|
173
|
+
if (shouldContinueRef.current) {
|
|
174
|
+
shouldContinueRef.current = false;
|
|
175
|
+
if (silenceTimerRef.current) {
|
|
176
|
+
clearTimeout(silenceTimerRef.current);
|
|
177
|
+
silenceTimerRef.current = null;
|
|
178
|
+
}
|
|
179
|
+
await Voice.stop();
|
|
180
|
+
onStop?.();
|
|
170
181
|
}
|
|
171
182
|
}, maxSilenceDuration);
|
|
172
183
|
}
|
|
@@ -174,7 +185,7 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
|
|
|
174
185
|
catch (e) {
|
|
175
186
|
const errorMessage = e instanceof Error ? e.message : 'Failed to start recording';
|
|
176
187
|
setError(errorMessage);
|
|
177
|
-
|
|
188
|
+
shouldContinueRef.current = false;
|
|
178
189
|
onError?.(errorMessage);
|
|
179
190
|
}
|
|
180
191
|
}, [
|
|
@@ -183,11 +194,11 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
|
|
|
183
194
|
onError,
|
|
184
195
|
continuous,
|
|
185
196
|
maxSilenceDuration,
|
|
186
|
-
|
|
197
|
+
onStop,
|
|
187
198
|
]);
|
|
188
199
|
const stop = useCallback(async () => {
|
|
189
200
|
try {
|
|
190
|
-
|
|
201
|
+
shouldContinueRef.current = false;
|
|
191
202
|
if (silenceTimerRef.current) {
|
|
192
203
|
clearTimeout(silenceTimerRef.current);
|
|
193
204
|
silenceTimerRef.current = null;
|
|
@@ -203,7 +214,7 @@ const VoiceMicrophone = ({ onSpeechResult, onPartialResult, onStart, onStop, onE
|
|
|
203
214
|
}, [onError, onStop]);
|
|
204
215
|
const cancel = useCallback(async () => {
|
|
205
216
|
try {
|
|
206
|
-
|
|
217
|
+
shouldContinueRef.current = false;
|
|
207
218
|
if (silenceTimerRef.current) {
|
|
208
219
|
clearTimeout(silenceTimerRef.current);
|
|
209
220
|
silenceTimerRef.current = null;
|
|
@@ -25,10 +25,30 @@ export interface UseVoiceRecognitionOptions {
|
|
|
25
25
|
* Callback fired when speech is recognized
|
|
26
26
|
*/
|
|
27
27
|
onResult?: (text: string) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Callback fired when partial results are available (real-time)
|
|
30
|
+
*/
|
|
31
|
+
onPartialResult?: (text: string) => void;
|
|
28
32
|
/**
|
|
29
33
|
* Callback fired when an error occurs
|
|
30
34
|
*/
|
|
31
35
|
onError?: (error: string) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Callback fired when recording starts
|
|
38
|
+
*/
|
|
39
|
+
onStart?: () => void;
|
|
40
|
+
/**
|
|
41
|
+
* Callback fired when recording ends
|
|
42
|
+
*/
|
|
43
|
+
onEnd?: () => void;
|
|
44
|
+
/**
|
|
45
|
+
* Callback fired when speech is recognized (before results)
|
|
46
|
+
*/
|
|
47
|
+
onRecognized?: () => void;
|
|
48
|
+
/**
|
|
49
|
+
* Callback fired when volume changes (0-10)
|
|
50
|
+
*/
|
|
51
|
+
onVolumeChanged?: (value: number) => void;
|
|
32
52
|
}
|
|
33
53
|
export interface UseVoiceRecognitionReturn {
|
|
34
54
|
/**
|
|
@@ -22,12 +22,12 @@ import Voice from '../index';
|
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
24
|
export const useVoiceRecognition = (options = {}) => {
|
|
25
|
-
const { locale = 'en-US', enablePartialResults = true, continuous = false, maxSilenceDuration = 5000, onResult, onError, } = options;
|
|
25
|
+
const { locale = 'en-US', enablePartialResults = true, continuous = false, maxSilenceDuration = 5000, onResult, onPartialResult, onError, onStart, onEnd, onRecognized, onVolumeChanged, } = options;
|
|
26
26
|
const [isRecording, setIsRecording] = useState(false);
|
|
27
27
|
const [results, setResults] = useState([]);
|
|
28
28
|
const [partialResults, setPartialResults] = useState([]);
|
|
29
29
|
const [error, setError] = useState(null);
|
|
30
|
-
const
|
|
30
|
+
const shouldContinueRef = React.useRef(false);
|
|
31
31
|
const silenceTimerRef = React.useRef(null);
|
|
32
32
|
const accumulatedTextRef = React.useRef('');
|
|
33
33
|
useEffect(() => {
|
|
@@ -46,13 +46,15 @@ export const useVoiceRecognition = (options = {}) => {
|
|
|
46
46
|
if (silenceTimerRef.current) {
|
|
47
47
|
clearTimeout(silenceTimerRef.current);
|
|
48
48
|
}
|
|
49
|
+
onStart?.();
|
|
49
50
|
};
|
|
50
51
|
Voice.onSpeechEnd = async () => {
|
|
51
52
|
setIsRecording(false);
|
|
53
|
+
onEnd?.();
|
|
52
54
|
// In continuous mode, restart listening after results
|
|
53
|
-
if (continuous &&
|
|
55
|
+
if (continuous && shouldContinueRef.current) {
|
|
54
56
|
setTimeout(async () => {
|
|
55
|
-
if (
|
|
57
|
+
if (shouldContinueRef.current) {
|
|
56
58
|
try {
|
|
57
59
|
await Voice.start(locale, {
|
|
58
60
|
EXTRA_PARTIAL_RESULTS: enablePartialResults,
|
|
@@ -65,11 +67,14 @@ export const useVoiceRecognition = (options = {}) => {
|
|
|
65
67
|
}, 100);
|
|
66
68
|
}
|
|
67
69
|
};
|
|
70
|
+
Voice.onSpeechRecognized = () => {
|
|
71
|
+
onRecognized?.();
|
|
72
|
+
};
|
|
68
73
|
Voice.onSpeechError = (e) => {
|
|
69
74
|
const errorMessage = e.error?.message || 'Unknown error';
|
|
70
75
|
setError(errorMessage);
|
|
71
76
|
setIsRecording(false);
|
|
72
|
-
|
|
77
|
+
shouldContinueRef.current = false;
|
|
73
78
|
if (silenceTimerRef.current) {
|
|
74
79
|
clearTimeout(silenceTimerRef.current);
|
|
75
80
|
}
|
|
@@ -95,22 +100,34 @@ export const useVoiceRecognition = (options = {}) => {
|
|
|
95
100
|
}
|
|
96
101
|
}
|
|
97
102
|
};
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
Voice.onSpeechPartialResults = (e) => {
|
|
104
|
+
if (e.value && e.value.length > 0) {
|
|
105
|
+
setPartialResults(e.value);
|
|
106
|
+
const firstPartial = e.value[0];
|
|
107
|
+
if (firstPartial) {
|
|
108
|
+
onPartialResult?.(firstPartial);
|
|
109
|
+
}
|
|
110
|
+
// Reset silence timer on partial results (user is speaking)
|
|
111
|
+
if (continuous && silenceTimerRef.current) {
|
|
112
|
+
clearTimeout(silenceTimerRef.current);
|
|
113
|
+
silenceTimerRef.current = setTimeout(async () => {
|
|
114
|
+
if (shouldContinueRef.current) {
|
|
115
|
+
shouldContinueRef.current = false;
|
|
116
|
+
if (silenceTimerRef.current) {
|
|
117
|
+
clearTimeout(silenceTimerRef.current);
|
|
118
|
+
silenceTimerRef.current = null;
|
|
108
119
|
}
|
|
109
|
-
|
|
110
|
-
|
|
120
|
+
await Voice.stop();
|
|
121
|
+
}
|
|
122
|
+
}, maxSilenceDuration);
|
|
111
123
|
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
Voice.onSpeechVolumeChanged = (e) => {
|
|
127
|
+
if (e.value !== undefined) {
|
|
128
|
+
onVolumeChanged?.(e.value);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
114
131
|
// Cleanup
|
|
115
132
|
return () => {
|
|
116
133
|
Voice.destroy().then(Voice.removeAllListeners);
|
|
@@ -118,9 +135,13 @@ export const useVoiceRecognition = (options = {}) => {
|
|
|
118
135
|
}, [
|
|
119
136
|
enablePartialResults,
|
|
120
137
|
onResult,
|
|
138
|
+
onPartialResult,
|
|
121
139
|
onError,
|
|
140
|
+
onStart,
|
|
141
|
+
onEnd,
|
|
142
|
+
onRecognized,
|
|
143
|
+
onVolumeChanged,
|
|
122
144
|
continuous,
|
|
123
|
-
shouldContinue,
|
|
124
145
|
locale,
|
|
125
146
|
maxSilenceDuration,
|
|
126
147
|
]);
|
|
@@ -132,7 +153,7 @@ export const useVoiceRecognition = (options = {}) => {
|
|
|
132
153
|
setPartialResults([]);
|
|
133
154
|
accumulatedTextRef.current = '';
|
|
134
155
|
}
|
|
135
|
-
|
|
156
|
+
shouldContinueRef.current = true;
|
|
136
157
|
// Check permission (Android only)
|
|
137
158
|
const hasPermission = await Voice.checkMicrophonePermission();
|
|
138
159
|
if (!hasPermission) {
|
|
@@ -147,9 +168,14 @@ export const useVoiceRecognition = (options = {}) => {
|
|
|
147
168
|
});
|
|
148
169
|
// Start silence timer if in continuous mode
|
|
149
170
|
if (continuous) {
|
|
150
|
-
silenceTimerRef.current = setTimeout(() => {
|
|
151
|
-
if (
|
|
152
|
-
|
|
171
|
+
silenceTimerRef.current = setTimeout(async () => {
|
|
172
|
+
if (shouldContinueRef.current) {
|
|
173
|
+
shouldContinueRef.current = false;
|
|
174
|
+
if (silenceTimerRef.current) {
|
|
175
|
+
clearTimeout(silenceTimerRef.current);
|
|
176
|
+
silenceTimerRef.current = null;
|
|
177
|
+
}
|
|
178
|
+
await Voice.stop();
|
|
153
179
|
}
|
|
154
180
|
}, maxSilenceDuration);
|
|
155
181
|
}
|
|
@@ -157,20 +183,13 @@ export const useVoiceRecognition = (options = {}) => {
|
|
|
157
183
|
catch (e) {
|
|
158
184
|
const errorMessage = e instanceof Error ? e.message : 'Failed to start recording';
|
|
159
185
|
setError(errorMessage);
|
|
160
|
-
|
|
186
|
+
shouldContinueRef.current = false;
|
|
161
187
|
onError?.(errorMessage);
|
|
162
188
|
}
|
|
163
|
-
}, [
|
|
164
|
-
locale,
|
|
165
|
-
enablePartialResults,
|
|
166
|
-
onError,
|
|
167
|
-
continuous,
|
|
168
|
-
maxSilenceDuration,
|
|
169
|
-
shouldContinue,
|
|
170
|
-
]);
|
|
189
|
+
}, [locale, enablePartialResults, onError, continuous, maxSilenceDuration]);
|
|
171
190
|
const stop = useCallback(async () => {
|
|
172
191
|
try {
|
|
173
|
-
|
|
192
|
+
shouldContinueRef.current = false;
|
|
174
193
|
if (silenceTimerRef.current) {
|
|
175
194
|
clearTimeout(silenceTimerRef.current);
|
|
176
195
|
silenceTimerRef.current = null;
|
|
@@ -185,7 +204,7 @@ export const useVoiceRecognition = (options = {}) => {
|
|
|
185
204
|
}, [onError]);
|
|
186
205
|
const cancel = useCallback(async () => {
|
|
187
206
|
try {
|
|
188
|
-
|
|
207
|
+
shouldContinueRef.current = false;
|
|
189
208
|
if (silenceTimerRef.current) {
|
|
190
209
|
clearTimeout(silenceTimerRef.current);
|
|
191
210
|
silenceTimerRef.current = null;
|
|
@@ -207,7 +226,7 @@ export const useVoiceRecognition = (options = {}) => {
|
|
|
207
226
|
setError(null);
|
|
208
227
|
setIsRecording(false);
|
|
209
228
|
accumulatedTextRef.current = '';
|
|
210
|
-
|
|
229
|
+
shouldContinueRef.current = false;
|
|
211
230
|
if (silenceTimerRef.current) {
|
|
212
231
|
clearTimeout(silenceTimerRef.current);
|
|
213
232
|
silenceTimerRef.current = null;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-voice-ts",
|
|
3
3
|
"description": "Advanced Speech-to-Text library for React Native with TypeScript support. Features ready-to-use components (VoiceMicrophone), custom hooks (useVoiceRecognition), real-time transcription, multi-language support, and comprehensive voice recognition capabilities for iOS and Android.",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.5",
|
|
5
5
|
"author": "Noor Mohammad <noor.jsdivs@gmail.com>",
|
|
6
6
|
"private": false,
|
|
7
7
|
"homepage": "https://github.com/noorjsdivs/react-native-voice-ts",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"lint:plugin": "eslint plugin/src/* --fix",
|
|
95
95
|
"clean": "rm -rf dist && rm -rf plugin/build",
|
|
96
96
|
"prepack": "yarn clean && yarn build && yarn build:plugin",
|
|
97
|
-
"test": "
|
|
97
|
+
"test": "echo \"No tests in library root. Run 'yarn --cwd example test' to test the example app.\"",
|
|
98
98
|
"validate": "yarn type-check && yarn lint:check && yarn format:check"
|
|
99
99
|
},
|
|
100
100
|
"dependencies": {
|