react-native-davoice-tts 1.0.74 → 1.0.76

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.
Files changed (18) hide show
  1. package/TTSRNBridge.podspec +2 -1
  2. package/ios/SpeechBridge/SpeechBridge.h +7 -0
  3. package/ios/SpeechBridge/SpeechBridge.m +220 -0
  4. package/ios/TTSRNBridge/DavoiceTTS.xcframework/Info.plist +5 -5
  5. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64/DavoiceTTS.framework/DavoiceTTS +0 -0
  6. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64/DavoiceTTS.framework/Modules/DavoiceTTS.swiftmodule/arm64-apple-ios.abi.json +191 -191
  7. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/DavoiceTTS +0 -0
  8. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/Modules/DavoiceTTS.swiftmodule/arm64-apple-ios-simulator.abi.json +1952 -1952
  9. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/Modules/DavoiceTTS.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +19 -19
  10. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/Modules/DavoiceTTS.swiftmodule/arm64-apple-ios-simulator.swiftinterface +19 -19
  11. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/Modules/DavoiceTTS.swiftmodule/x86_64-apple-ios-simulator.abi.json +1952 -1952
  12. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/Modules/DavoiceTTS.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +19 -19
  13. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/Modules/DavoiceTTS.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +19 -19
  14. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/_CodeSignature/CodeDirectory +0 -0
  15. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/_CodeSignature/CodeRequirements-1 +0 -0
  16. package/ios/TTSRNBridge/DavoiceTTS.xcframework/ios-arm64_x86_64-simulator/DavoiceTTS.framework/_CodeSignature/CodeResources +24 -24
  17. package/package.json +2 -1
  18. package/speech/index.ts +314 -0
@@ -14,25 +14,6 @@ import _StringProcessing
14
14
  import _SwiftConcurrencyShims
15
15
  import onnxruntime_objc
16
16
  import phonemes
17
- @objc public protocol STTDelegate {
18
- @objc func stt(_ stt: DavoiceTTS.STT, didEmitEvent name: Swift.String, body: [Swift.String : Any]?)
19
- }
20
- @objc @_inheritsConvenienceInitializers @objcMembers final public class STT : ObjectiveC.NSObject, Speech.SFSpeechRecognizerDelegate {
21
- @objc weak final public var delegate: (any DavoiceTTS.STTDelegate)?
22
- @objc final public var continuous: Swift.Bool
23
- @objc public static let supportedEvents: [Swift.String]
24
- @objc final public func isSpeechAvailable(_ completion: @escaping (Swift.Bool) -> Swift.Void)
25
- @objc final public func isRecognizing() -> Swift.Bool
26
- @objc final public func startSpeech(localeStr: Swift.String?)
27
- @objc final public func stopSpeech(_ completion: ((Swift.Bool) -> Swift.Void)? = nil)
28
- @objc final public func cancelSpeech(_ completion: ((Swift.Bool) -> Swift.Void)? = nil)
29
- @objc final public func destroySpeech(_ completion: ((Swift.Bool) -> Swift.Void)? = nil)
30
- @objc final public func teardown()
31
- @objc final public func teardown2()
32
- @objc final public func speechRecognizer(_ speechRecognizer: Speech.SFSpeechRecognizer, availabilityDidChange available: Swift.Bool)
33
- @objc override dynamic public init()
34
- @objc deinit
35
- }
36
17
  @objc @_inheritsConvenienceInitializers @_hasMissingDesignatedInitializers final public class SwiftSoundQueue : ObjectiveC.NSObject, AVFAudio.AVAudioPlayerDelegate {
37
18
  @objc deinit
38
19
  public static let shared: DavoiceTTS.SwiftSoundQueue
@@ -69,3 +50,22 @@ public enum AudioPlaybackHook {
69
50
  public static var isEngineReady: DavoiceTTS.IsEngineReady?
70
51
  public static var useOnlyEnginePlayback: DavoiceTTS.useOnlyEnginePlayback?
71
52
  }
53
+ @objc public protocol STTDelegate {
54
+ @objc func stt(_ stt: DavoiceTTS.STT, didEmitEvent name: Swift.String, body: [Swift.String : Any]?)
55
+ }
56
+ @objc @_inheritsConvenienceInitializers @objcMembers final public class STT : ObjectiveC.NSObject, Speech.SFSpeechRecognizerDelegate {
57
+ @objc weak final public var delegate: (any DavoiceTTS.STTDelegate)?
58
+ @objc final public var continuous: Swift.Bool
59
+ @objc public static let supportedEvents: [Swift.String]
60
+ @objc final public func isSpeechAvailable(_ completion: @escaping (Swift.Bool) -> Swift.Void)
61
+ @objc final public func isRecognizing() -> Swift.Bool
62
+ @objc final public func startSpeech(localeStr: Swift.String?)
63
+ @objc final public func stopSpeech(_ completion: ((Swift.Bool) -> Swift.Void)? = nil)
64
+ @objc final public func cancelSpeech(_ completion: ((Swift.Bool) -> Swift.Void)? = nil)
65
+ @objc final public func destroySpeech(_ completion: ((Swift.Bool) -> Swift.Void)? = nil)
66
+ @objc final public func teardown()
67
+ @objc final public func teardown2()
68
+ @objc final public func speechRecognizer(_ speechRecognizer: Speech.SFSpeechRecognizer, availabilityDidChange available: Swift.Bool)
69
+ @objc override dynamic public init()
70
+ @objc deinit
71
+ }
@@ -14,25 +14,6 @@ import _StringProcessing
14
14
  import _SwiftConcurrencyShims
15
15
  import onnxruntime_objc
16
16
  import phonemes
17
- @objc public protocol STTDelegate {
18
- @objc func stt(_ stt: DavoiceTTS.STT, didEmitEvent name: Swift.String, body: [Swift.String : Any]?)
19
- }
20
- @objc @_inheritsConvenienceInitializers @objcMembers final public class STT : ObjectiveC.NSObject, Speech.SFSpeechRecognizerDelegate {
21
- @objc weak final public var delegate: (any DavoiceTTS.STTDelegate)?
22
- @objc final public var continuous: Swift.Bool
23
- @objc public static let supportedEvents: [Swift.String]
24
- @objc final public func isSpeechAvailable(_ completion: @escaping (Swift.Bool) -> Swift.Void)
25
- @objc final public func isRecognizing() -> Swift.Bool
26
- @objc final public func startSpeech(localeStr: Swift.String?)
27
- @objc final public func stopSpeech(_ completion: ((Swift.Bool) -> Swift.Void)? = nil)
28
- @objc final public func cancelSpeech(_ completion: ((Swift.Bool) -> Swift.Void)? = nil)
29
- @objc final public func destroySpeech(_ completion: ((Swift.Bool) -> Swift.Void)? = nil)
30
- @objc final public func teardown()
31
- @objc final public func teardown2()
32
- @objc final public func speechRecognizer(_ speechRecognizer: Speech.SFSpeechRecognizer, availabilityDidChange available: Swift.Bool)
33
- @objc override dynamic public init()
34
- @objc deinit
35
- }
36
17
  @objc @_inheritsConvenienceInitializers @_hasMissingDesignatedInitializers final public class SwiftSoundQueue : ObjectiveC.NSObject, AVFAudio.AVAudioPlayerDelegate {
37
18
  @objc deinit
38
19
  public static let shared: DavoiceTTS.SwiftSoundQueue
@@ -69,3 +50,22 @@ public enum AudioPlaybackHook {
69
50
  public static var isEngineReady: DavoiceTTS.IsEngineReady?
70
51
  public static var useOnlyEnginePlayback: DavoiceTTS.useOnlyEnginePlayback?
71
52
  }
53
+ @objc public protocol STTDelegate {
54
+ @objc func stt(_ stt: DavoiceTTS.STT, didEmitEvent name: Swift.String, body: [Swift.String : Any]?)
55
+ }
56
+ @objc @_inheritsConvenienceInitializers @objcMembers final public class STT : ObjectiveC.NSObject, Speech.SFSpeechRecognizerDelegate {
57
+ @objc weak final public var delegate: (any DavoiceTTS.STTDelegate)?
58
+ @objc final public var continuous: Swift.Bool
59
+ @objc public static let supportedEvents: [Swift.String]
60
+ @objc final public func isSpeechAvailable(_ completion: @escaping (Swift.Bool) -> Swift.Void)
61
+ @objc final public func isRecognizing() -> Swift.Bool
62
+ @objc final public func startSpeech(localeStr: Swift.String?)
63
+ @objc final public func stopSpeech(_ completion: ((Swift.Bool) -> Swift.Void)? = nil)
64
+ @objc final public func cancelSpeech(_ completion: ((Swift.Bool) -> Swift.Void)? = nil)
65
+ @objc final public func destroySpeech(_ completion: ((Swift.Bool) -> Swift.Void)? = nil)
66
+ @objc final public func teardown()
67
+ @objc final public func teardown2()
68
+ @objc final public func speechRecognizer(_ speechRecognizer: Speech.SFSpeechRecognizer, availabilityDidChange available: Swift.Bool)
69
+ @objc override dynamic public init()
70
+ @objc deinit
71
+ }
@@ -18,11 +18,11 @@
18
18
  </data>
19
19
  <key>Modules/DavoiceTTS.swiftmodule/arm64-apple-ios-simulator.abi.json</key>
20
20
  <data>
21
- Ux06wJCscUYxESolNVnOYjBZxxg=
21
+ fivfg8eVNZsAD8O5/A9Y+uONgEE=
22
22
  </data>
23
23
  <key>Modules/DavoiceTTS.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface</key>
24
24
  <data>
25
- iZrHLJK4/9M/e49uphvsLsM0j1w=
25
+ 8Y3XQLeIfwaoU+7uGOI/ejnQmzU=
26
26
  </data>
27
27
  <key>Modules/DavoiceTTS.swiftmodule/arm64-apple-ios-simulator.swiftdoc</key>
28
28
  <data>
@@ -30,19 +30,19 @@
30
30
  </data>
31
31
  <key>Modules/DavoiceTTS.swiftmodule/arm64-apple-ios-simulator.swiftinterface</key>
32
32
  <data>
33
- iZrHLJK4/9M/e49uphvsLsM0j1w=
33
+ 8Y3XQLeIfwaoU+7uGOI/ejnQmzU=
34
34
  </data>
35
35
  <key>Modules/DavoiceTTS.swiftmodule/arm64-apple-ios-simulator.swiftmodule</key>
36
36
  <data>
37
- O7su1nukOnT6jmDHhZnVr+HSFX0=
37
+ bqdpNARZYCm+k3I6TD9apxWxOo8=
38
38
  </data>
39
39
  <key>Modules/DavoiceTTS.swiftmodule/x86_64-apple-ios-simulator.abi.json</key>
40
40
  <data>
41
- Ux06wJCscUYxESolNVnOYjBZxxg=
41
+ fivfg8eVNZsAD8O5/A9Y+uONgEE=
42
42
  </data>
43
43
  <key>Modules/DavoiceTTS.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface</key>
44
44
  <data>
45
- euHAtwrs/moRnmtThKWKOEePrKk=
45
+ OdKnfC2sehdVMtSqGdsJsUvKDy0=
46
46
  </data>
47
47
  <key>Modules/DavoiceTTS.swiftmodule/x86_64-apple-ios-simulator.swiftdoc</key>
48
48
  <data>
@@ -50,11 +50,11 @@
50
50
  </data>
51
51
  <key>Modules/DavoiceTTS.swiftmodule/x86_64-apple-ios-simulator.swiftinterface</key>
52
52
  <data>
53
- euHAtwrs/moRnmtThKWKOEePrKk=
53
+ OdKnfC2sehdVMtSqGdsJsUvKDy0=
54
54
  </data>
55
55
  <key>Modules/DavoiceTTS.swiftmodule/x86_64-apple-ios-simulator.swiftmodule</key>
56
56
  <data>
57
- S6id03wM8kk3s8CorbHArkYtlag=
57
+ pbGalSuDKtFBK6ju5uqFtv4nWj4=
58
58
  </data>
59
59
  <key>Modules/module.modulemap</key>
60
60
  <data>
@@ -89,22 +89,22 @@
89
89
  <dict>
90
90
  <key>hash</key>
91
91
  <data>
92
- Ux06wJCscUYxESolNVnOYjBZxxg=
92
+ fivfg8eVNZsAD8O5/A9Y+uONgEE=
93
93
  </data>
94
94
  <key>hash2</key>
95
95
  <data>
96
- WMZrtuiNIaxEQhienaCdktvQj4fk8rLubfpq8Q+6Kl4=
96
+ rNQyPRNJMuApbMq1gyjjFmGFsLiDnRoxQgK1WWhckY8=
97
97
  </data>
98
98
  </dict>
99
99
  <key>Modules/DavoiceTTS.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface</key>
100
100
  <dict>
101
101
  <key>hash</key>
102
102
  <data>
103
- iZrHLJK4/9M/e49uphvsLsM0j1w=
103
+ 8Y3XQLeIfwaoU+7uGOI/ejnQmzU=
104
104
  </data>
105
105
  <key>hash2</key>
106
106
  <data>
107
- jgkwM08SMKGxS1bQJU54pz5e45PKuPoCD6zGbV/A0II=
107
+ 4/BSQSjWbbppLdBvyxu62gblBNBKQ/MoCJuBC5OioL4=
108
108
  </data>
109
109
  </dict>
110
110
  <key>Modules/DavoiceTTS.swiftmodule/arm64-apple-ios-simulator.swiftdoc</key>
@@ -122,44 +122,44 @@
122
122
  <dict>
123
123
  <key>hash</key>
124
124
  <data>
125
- iZrHLJK4/9M/e49uphvsLsM0j1w=
125
+ 8Y3XQLeIfwaoU+7uGOI/ejnQmzU=
126
126
  </data>
127
127
  <key>hash2</key>
128
128
  <data>
129
- jgkwM08SMKGxS1bQJU54pz5e45PKuPoCD6zGbV/A0II=
129
+ 4/BSQSjWbbppLdBvyxu62gblBNBKQ/MoCJuBC5OioL4=
130
130
  </data>
131
131
  </dict>
132
132
  <key>Modules/DavoiceTTS.swiftmodule/arm64-apple-ios-simulator.swiftmodule</key>
133
133
  <dict>
134
134
  <key>hash</key>
135
135
  <data>
136
- O7su1nukOnT6jmDHhZnVr+HSFX0=
136
+ bqdpNARZYCm+k3I6TD9apxWxOo8=
137
137
  </data>
138
138
  <key>hash2</key>
139
139
  <data>
140
- CTLYrs6X2gnFcahbYjHwbD5HamlDtV8IOFsduYjf/Ac=
140
+ IvVtse9l29epDyofiapE3u4ODUSbx16fxMnK+B1W0NA=
141
141
  </data>
142
142
  </dict>
143
143
  <key>Modules/DavoiceTTS.swiftmodule/x86_64-apple-ios-simulator.abi.json</key>
144
144
  <dict>
145
145
  <key>hash</key>
146
146
  <data>
147
- Ux06wJCscUYxESolNVnOYjBZxxg=
147
+ fivfg8eVNZsAD8O5/A9Y+uONgEE=
148
148
  </data>
149
149
  <key>hash2</key>
150
150
  <data>
151
- WMZrtuiNIaxEQhienaCdktvQj4fk8rLubfpq8Q+6Kl4=
151
+ rNQyPRNJMuApbMq1gyjjFmGFsLiDnRoxQgK1WWhckY8=
152
152
  </data>
153
153
  </dict>
154
154
  <key>Modules/DavoiceTTS.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface</key>
155
155
  <dict>
156
156
  <key>hash</key>
157
157
  <data>
158
- euHAtwrs/moRnmtThKWKOEePrKk=
158
+ OdKnfC2sehdVMtSqGdsJsUvKDy0=
159
159
  </data>
160
160
  <key>hash2</key>
161
161
  <data>
162
- yRNxpR+3xAUB0BH5qbJ39I3Nok/3xDHogkzauX28pTI=
162
+ Oxh3jQ93jMvHo99sWfY1bBtwfF5x/EbM+GhI0EugZs8=
163
163
  </data>
164
164
  </dict>
165
165
  <key>Modules/DavoiceTTS.swiftmodule/x86_64-apple-ios-simulator.swiftdoc</key>
@@ -177,22 +177,22 @@
177
177
  <dict>
178
178
  <key>hash</key>
179
179
  <data>
180
- euHAtwrs/moRnmtThKWKOEePrKk=
180
+ OdKnfC2sehdVMtSqGdsJsUvKDy0=
181
181
  </data>
182
182
  <key>hash2</key>
183
183
  <data>
184
- yRNxpR+3xAUB0BH5qbJ39I3Nok/3xDHogkzauX28pTI=
184
+ Oxh3jQ93jMvHo99sWfY1bBtwfF5x/EbM+GhI0EugZs8=
185
185
  </data>
186
186
  </dict>
187
187
  <key>Modules/DavoiceTTS.swiftmodule/x86_64-apple-ios-simulator.swiftmodule</key>
188
188
  <dict>
189
189
  <key>hash</key>
190
190
  <data>
191
- S6id03wM8kk3s8CorbHArkYtlag=
191
+ pbGalSuDKtFBK6ju5uqFtv4nWj4=
192
192
  </data>
193
193
  <key>hash2</key>
194
194
  <data>
195
- DHu3JYtuwEbXh/fa6dCr248uI6dsUDvxPAtnNUmYNXc=
195
+ XhUEJ5zGDgJwSvZwDv4OAX9lfe0uT3U2LpyFR+C5tNI=
196
196
  </data>
197
197
  </dict>
198
198
  <key>Modules/module.modulemap</key>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-davoice-tts",
3
- "version": "1.0.74",
3
+ "version": "1.0.76",
4
4
  "description": "tts library for React Native",
5
5
  "main": "tts/index.js",
6
6
  "types": "tts/index.d.ts",
@@ -12,6 +12,7 @@
12
12
  "files": [
13
13
  "tts/",
14
14
  "stt/",
15
+ "speech/",
15
16
  "android/",
16
17
  "ios/",
17
18
  "phonemes_dir",
@@ -0,0 +1,314 @@
1
+ import { NativeModules, NativeEventEmitter, Platform } from 'react-native';
2
+
3
+ // Native handles
4
+ const NativeSpeech = NativeModules.SpeechBridge; // iOS unified (if present)
5
+ const NativeSTT =
6
+ NativeModules.STT ||
7
+ NativeModules.RCTSTT ||
8
+ NativeModules.Voice ||
9
+ NativeModules.RCTVoice;
10
+ const NativeTTS = NativeModules.DaVoiceTTSBridge;
11
+
12
+ // ---- Types ----
13
+ export type SpeechStartEvent = {};
14
+ export type SpeechEndEvent = {};
15
+ export type SpeechRecognizedEvent = { isFinal: boolean };
16
+ export type SpeechErrorEvent = { error: { code?: string; message?: string } };
17
+ export type SpeechResultsEvent = { value: string[] };
18
+ export type SpeechVolumeChangeEvent = { value: number };
19
+
20
+ export type UnifiedEvents = {
21
+ // STT
22
+ onSpeechStart?: (e: SpeechStartEvent) => void;
23
+ onSpeechRecognized?: (e: SpeechRecognizedEvent) => void;
24
+ onSpeechEnd?: (e: SpeechEndEvent) => void;
25
+ onSpeechError?: (e: SpeechErrorEvent) => void;
26
+ onSpeechResults?: (e: SpeechResultsEvent) => void;
27
+ onSpeechPartialResults?: (e: SpeechResultsEvent) => void;
28
+ onSpeechVolumeChanged?: (e: SpeechVolumeChangeEvent) => void;
29
+ // TTS
30
+ onFinishedSpeaking?: () => void;
31
+ };
32
+
33
+ type NativeEventName =
34
+ | 'onSpeechStart'
35
+ | 'onSpeechRecognized'
36
+ | 'onSpeechEnd'
37
+ | 'onSpeechError'
38
+ | 'onSpeechResults'
39
+ | 'onSpeechPartialResults'
40
+ | 'onSpeechVolumeChanged'
41
+ | 'onFinishedSpeaking';
42
+
43
+ class Speech {
44
+ private sttEmitter: NativeEventEmitter | null = null;
45
+ private ttsEmitter: NativeEventEmitter | null = null;
46
+ private unifiedEmitter: NativeEventEmitter | null = null;
47
+ private subs: Array<{ remove: () => void }> = [];
48
+ private handlers: Required<UnifiedEvents>;
49
+
50
+ constructor() {
51
+ this.handlers = {
52
+ onSpeechStart: () => {},
53
+ onSpeechRecognized: () => {},
54
+ onSpeechEnd: () => {},
55
+ onSpeechError: () => {},
56
+ onSpeechResults: () => {},
57
+ onSpeechPartialResults: () => {},
58
+ onSpeechVolumeChanged: () => {},
59
+ onFinishedSpeaking: () => {},
60
+ };
61
+
62
+ // Emitters per-platform
63
+ if (Platform.OS !== 'web') {
64
+ if (Platform.OS === 'ios' && NativeSpeech) {
65
+ this.unifiedEmitter = new NativeEventEmitter(NativeSpeech);
66
+ } else {
67
+ // Android (and iOS fallback): separate modules
68
+ if (NativeSTT) this.sttEmitter = new NativeEventEmitter(NativeSTT);
69
+ if (NativeTTS) this.ttsEmitter = new NativeEventEmitter(NativeTTS);
70
+ }
71
+ }
72
+ }
73
+
74
+ // ---------- Init / Destroy ----------
75
+ /**
76
+ * iOS: initialize STT then TTS via native SpeechBridge if available.
77
+ * Android: no special init needed; optionally preload TTS (if you want).
78
+ */
79
+ async initAll(opts: { locale: string; model: string; timeoutMs?: number }) {
80
+ if (Platform.OS === 'ios' && NativeSpeech?.initAll) {
81
+ return NativeSpeech.initAll(opts);
82
+ }
83
+
84
+ // Fallback (Android or iOS w/o SpeechBridge):
85
+ // 1) Start STT (engine hot will happen internally); 2) init TTS.
86
+ if (!NativeSTT || !NativeTTS) {
87
+ throw new Error('Missing native bridges (STT/TTS).');
88
+ }
89
+
90
+ // Start STT (best-effort; no-op if already running)
91
+ await new Promise<void>((resolve, reject) => {
92
+ try {
93
+ // iOS fallback signature: (locale, cb)
94
+ // Android signature: (locale, extras, cb)
95
+ if (Platform.OS === 'android' && NativeSTT.startSpeech.length >= 3) {
96
+ NativeSTT.startSpeech(
97
+ opts.locale,
98
+ {
99
+ EXTRA_LANGUAGE_MODEL: 'LANGUAGE_MODEL_FREE_FORM',
100
+ EXTRA_MAX_RESULTS: 5,
101
+ EXTRA_PARTIAL_RESULTS: true,
102
+ REQUEST_PERMISSIONS_AUTO: true,
103
+ },
104
+ (err: string) => (err ? reject(new Error(err)) : resolve()),
105
+ );
106
+ } else {
107
+ NativeSTT.startSpeech(opts.locale, (err: string) =>
108
+ err ? reject(new Error(err)) : resolve(),
109
+ );
110
+ }
111
+ } catch (e) {
112
+ reject(e as any);
113
+ }
114
+ });
115
+
116
+ // Init TTS
117
+ await NativeTTS.initTTS({ model: opts.model });
118
+ }
119
+
120
+ async destroyAll() {
121
+ // iOS unified
122
+ if (Platform.OS === 'ios' && NativeSpeech?.destroyAll) {
123
+ const r = await NativeSpeech.destroyAll();
124
+ this.teardownListeners();
125
+ return r;
126
+ }
127
+ // Fallback: destroy TTS -> STT
128
+ try { await NativeTTS?.destroy?.(); } catch {}
129
+ try {
130
+ await new Promise<void>((res) => {
131
+ if (!NativeSTT?.destroySpeech) return res();
132
+ NativeSTT.destroySpeech(() => res());
133
+ });
134
+ } catch {}
135
+ this.teardownListeners();
136
+ return 'Destroyed';
137
+ }
138
+
139
+ // ---------- STT ----------
140
+ async start(locale: string, options: Record<string, any> = {}) {
141
+ this.ensureListeners();
142
+ // Prefer unified on iOS
143
+ if (Platform.OS === 'ios' && NativeSpeech?.startSpeech) {
144
+ return new Promise<void>((resolve) => NativeSpeech.startSpeech(locale, () => resolve()));
145
+ }
146
+
147
+ // Android + iOS fallback
148
+ return new Promise<void>((resolve, reject) => {
149
+ if (!NativeSTT?.startSpeech) return reject(new Error('startSpeech not available'));
150
+ if (Platform.OS === 'android' && NativeSTT.startSpeech.length >= 3) {
151
+ NativeSTT.startSpeech(
152
+ locale,
153
+ {
154
+ EXTRA_LANGUAGE_MODEL: 'LANGUAGE_MODEL_FREE_FORM',
155
+ EXTRA_MAX_RESULTS: 5,
156
+ EXTRA_PARTIAL_RESULTS: true,
157
+ REQUEST_PERMISSIONS_AUTO: true,
158
+ ...options,
159
+ },
160
+ (err: string) => (err ? reject(new Error(err)) : resolve()),
161
+ );
162
+ } else {
163
+ NativeSTT.startSpeech(locale, (err: string) =>
164
+ err ? reject(new Error(err)) : resolve(),
165
+ );
166
+ }
167
+ });
168
+ }
169
+
170
+ stop(): Promise<void> {
171
+ if (Platform.OS === 'ios' && NativeSpeech?.stopSpeech) {
172
+ return new Promise((res) => NativeSpeech.stopSpeech(() => res()));
173
+ }
174
+ if (!NativeSTT?.stopSpeech) return Promise.resolve();
175
+ return new Promise((res) => NativeSTT.stopSpeech(() => res()));
176
+ }
177
+
178
+ cancel(): Promise<void> {
179
+ if (Platform.OS === 'ios' && NativeSpeech?.cancelSpeech) {
180
+ return new Promise((res) => NativeSpeech.cancelSpeech(() => res()));
181
+ }
182
+ if (!NativeSTT?.cancelSpeech) return Promise.resolve();
183
+ return new Promise((res) => NativeSTT.cancelSpeech(() => res()));
184
+ }
185
+
186
+ isAvailable(): Promise<0 | 1> {
187
+ // Prefer unified
188
+ if (Platform.OS === 'ios' && NativeSpeech?.isSpeechAvailable) {
189
+ return new Promise((resolve, reject) =>
190
+ NativeSpeech.isSpeechAvailable((ok: 0 | 1, err: string) =>
191
+ err ? reject(new Error(err)) : resolve(ok),
192
+ ),
193
+ );
194
+ }
195
+ if (NativeSTT?.isSpeechAvailable) {
196
+ return new Promise((resolve) =>
197
+ NativeSTT.isSpeechAvailable((ok: 0 | 1) => resolve(ok)),
198
+ );
199
+ }
200
+ return Promise.resolve(1);
201
+ }
202
+
203
+ isRecognizing(): Promise<0 | 1> {
204
+ if (Platform.OS === 'ios' && NativeSpeech?.isRecognizing) {
205
+ return new Promise((resolve) =>
206
+ NativeSpeech.isRecognizing((v: 0 | 1) => resolve(v)),
207
+ );
208
+ }
209
+ if (NativeSTT?.isRecognizing) {
210
+ return new Promise((resolve) =>
211
+ NativeSTT.isRecognizing((v: 0 | 1) => resolve(v)),
212
+ );
213
+ }
214
+ return Promise.resolve(0);
215
+ }
216
+
217
+ // ---------- TTS ----------
218
+ async initTTS(modelOrConfig: string | { model: string }) {
219
+ // iOS unified asks you to use initAll
220
+ if (Platform.OS === 'ios' && NativeSpeech?.initAll) {
221
+ throw new Error('Use initAll() on iOS unified bridge.');
222
+ }
223
+ const cfg = typeof modelOrConfig === 'string' ? { model: modelOrConfig } : modelOrConfig;
224
+ if (!cfg?.model) throw new Error("initTTS: missing 'model'");
225
+ return NativeTTS.initTTS(cfg);
226
+ }
227
+
228
+ async speak(text: string, speakerId = 0) {
229
+ if (Platform.OS === 'ios' && NativeSpeech?.speak) {
230
+ return NativeSpeech.speak(text, speakerId);
231
+ }
232
+ if (!NativeTTS?.speak) throw new Error('TTS speak not available');
233
+ return NativeTTS.speak(text, speakerId);
234
+ }
235
+
236
+ async stopSpeaking() {
237
+ if (Platform.OS === 'ios' && NativeSpeech?.stopSpeaking) {
238
+ return NativeSpeech.stopSpeaking();
239
+ }
240
+ if (!NativeTTS?.stopSpeaking) return;
241
+ return NativeTTS.stopSpeaking();
242
+ }
243
+
244
+ // ---------- Events ----------
245
+ private ensureListeners() {
246
+ if (this.subs.length) return;
247
+
248
+ // iOS unified: subscribe once on the unified emitter
249
+ if (Platform.OS === 'ios' && this.unifiedEmitter) {
250
+ const map: Record<NativeEventName, (...args: any[]) => void> = {
251
+ onSpeechStart: (e) => this.handlers.onSpeechStart(e),
252
+ onSpeechRecognized: (e) => this.handlers.onSpeechRecognized(e),
253
+ onSpeechEnd: (e) => this.handlers.onSpeechEnd(e),
254
+ onSpeechError: (e) => this.handlers.onSpeechError(e),
255
+ onSpeechResults: (e) => this.handlers.onSpeechResults(e),
256
+ onSpeechPartialResults: (e) => this.handlers.onSpeechPartialResults(e),
257
+ onSpeechVolumeChanged: (e) => this.handlers.onSpeechVolumeChanged(e),
258
+ onFinishedSpeaking: () => this.handlers.onFinishedSpeaking(),
259
+ };
260
+ (Object.keys(map) as NativeEventName[]).forEach((name) => {
261
+ try {
262
+ const sub = this.unifiedEmitter!.addListener(name, map[name]);
263
+ this.subs.push(sub);
264
+ } catch {}
265
+ });
266
+ return;
267
+ }
268
+
269
+ // Android (and iOS fallback): subscribe to both STT and TTS emitters
270
+ if (this.sttEmitter) {
271
+ const sttMap = {
272
+ onSpeechStart: (e: any) => this.handlers.onSpeechStart(e),
273
+ onSpeechRecognized: (e: any) => this.handlers.onSpeechRecognized(e),
274
+ onSpeechEnd: (e: any) => this.handlers.onSpeechEnd(e),
275
+ onSpeechError: (e: any) => this.handlers.onSpeechError(e),
276
+ onSpeechResults: (e: any) => this.handlers.onSpeechResults(e),
277
+ onSpeechPartialResults: (e: any) => this.handlers.onSpeechPartialResults(e),
278
+ onSpeechVolumeChanged: (e: any) => this.handlers.onSpeechVolumeChanged(e),
279
+ };
280
+ (Object.keys(sttMap) as (keyof typeof sttMap)[]).forEach((name) => {
281
+ try {
282
+ const sub = this.sttEmitter!.addListener(name, sttMap[name]);
283
+ this.subs.push(sub);
284
+ } catch {}
285
+ });
286
+ }
287
+ if (this.ttsEmitter) {
288
+ try {
289
+ const sub = this.ttsEmitter.addListener('onFinishedSpeaking', () =>
290
+ this.handlers.onFinishedSpeaking(),
291
+ );
292
+ this.subs.push(sub);
293
+ } catch {}
294
+ }
295
+ }
296
+
297
+ private teardownListeners() {
298
+ this.subs.forEach(s => { try { s.remove(); } catch {} });
299
+ this.subs = [];
300
+ }
301
+
302
+ // ---------- Friendly setters ----------
303
+ set onSpeechStart(fn: (e: SpeechStartEvent) => void) { this.handlers.onSpeechStart = fn; this.ensureListeners(); }
304
+ set onSpeechRecognized(fn: (e: SpeechRecognizedEvent) => void) { this.handlers.onSpeechRecognized = fn; this.ensureListeners(); }
305
+ set onSpeechEnd(fn: (e: SpeechEndEvent) => void) { this.handlers.onSpeechEnd = fn; this.ensureListeners(); }
306
+ set onSpeechError(fn: (e: SpeechErrorEvent) => void) { this.handlers.onSpeechError = fn; this.ensureListeners(); }
307
+ set onSpeechResults(fn: (e: SpeechResultsEvent) => void) { this.handlers.onSpeechResults = fn; this.ensureListeners(); }
308
+ set onSpeechPartialResults(fn: (e: SpeechResultsEvent) => void) { this.handlers.onSpeechPartialResults = fn; this.ensureListeners(); }
309
+ set onSpeechVolumeChanged(fn: (e: SpeechVolumeChangeEvent) => void) { this.handlers.onSpeechVolumeChanged = fn; this.ensureListeners(); }
310
+ set onFinishedSpeaking(fn: () => void) { this.handlers.onFinishedSpeaking = fn; this.ensureListeners(); }
311
+ }
312
+
313
+ const SpeechInstance = new Speech();
314
+ export default SpeechInstance;