@gmessier/nitro-speech 0.3.3 → 0.4.0

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 (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +165 -148
  3. package/android/build.gradle +0 -1
  4. package/android/src/main/cpp/cpp-adapter.cpp +5 -1
  5. package/android/src/main/java/com/margelo/nitro/nitrospeech/HybridNitroSpeech.kt +2 -0
  6. package/android/src/main/java/com/margelo/nitro/nitrospeech/recognizer/AutoStopper.kt +80 -16
  7. package/android/src/main/java/com/margelo/nitro/nitrospeech/recognizer/HybridRecognizer.kt +93 -20
  8. package/android/src/main/java/com/margelo/nitro/nitrospeech/recognizer/RecognitionListenerSession.kt +27 -15
  9. package/ios/{BufferUtil.swift → Audio/AudioBufferConverter.swift} +3 -34
  10. package/ios/Audio/AudioLevelTracker.swift +66 -0
  11. package/ios/Coordinator.swift +105 -0
  12. package/ios/Engines/AnalyzerEngine.swift +241 -0
  13. package/ios/Engines/DictationRuntime.swift +67 -0
  14. package/ios/Engines/RecognizerEngine.swift +312 -0
  15. package/ios/Engines/SFSpeechEngine.swift +119 -0
  16. package/ios/Engines/SpeechRuntime.swift +58 -0
  17. package/ios/Engines/TranscriberRuntimeProtocol.swift +21 -0
  18. package/ios/HybridNitroSpeech.swift +1 -10
  19. package/ios/HybridRecognizer.swift +135 -192
  20. package/ios/LocaleManager.swift +73 -0
  21. package/ios/{AppStateObserver.swift → Shared/AppStateObserver.swift} +1 -2
  22. package/ios/Shared/AutoStopper.swift +147 -0
  23. package/ios/Shared/HapticImpact.swift +24 -0
  24. package/ios/Shared/Log.swift +41 -0
  25. package/ios/Shared/Permissions.swift +59 -0
  26. package/ios/Shared/Utils.swift +58 -0
  27. package/lib/NitroSpeech.d.ts +2 -0
  28. package/lib/NitroSpeech.js +2 -0
  29. package/lib/Recognizer/RecognizerRef.d.ts +5 -0
  30. package/lib/Recognizer/RecognizerRef.js +13 -0
  31. package/lib/Recognizer/SpeechRecognizer.d.ts +8 -0
  32. package/lib/Recognizer/SpeechRecognizer.js +9 -0
  33. package/lib/Recognizer/methods.d.ts +8 -0
  34. package/lib/Recognizer/methods.js +29 -0
  35. package/lib/Recognizer/types.d.ts +6 -0
  36. package/lib/Recognizer/types.js +1 -0
  37. package/lib/Recognizer/useRecognizer.d.ts +16 -0
  38. package/lib/Recognizer/useRecognizer.js +71 -0
  39. package/lib/Recognizer/useVoiceInputVolume.d.ts +25 -0
  40. package/lib/Recognizer/useVoiceInputVolume.js +52 -0
  41. package/lib/index.d.ts +6 -0
  42. package/lib/index.js +6 -0
  43. package/lib/specs/NitroSpeech.nitro.d.ts +8 -0
  44. package/lib/specs/NitroSpeech.nitro.js +1 -0
  45. package/lib/specs/Recognizer.nitro.d.ts +95 -0
  46. package/lib/specs/Recognizer.nitro.js +1 -0
  47. package/lib/specs/SpeechRecognitionConfig.d.ts +162 -0
  48. package/lib/specs/SpeechRecognitionConfig.js +1 -0
  49. package/lib/specs/VolumeChangeEvent.d.ts +31 -0
  50. package/lib/specs/VolumeChangeEvent.js +1 -0
  51. package/nitro.json +0 -4
  52. package/nitrogen/generated/android/NitroSpeech+autolinking.cmake +2 -2
  53. package/nitrogen/generated/android/NitroSpeechOnLoad.cpp +4 -2
  54. package/nitrogen/generated/android/c++/JFunc_void_VolumeChangeEvent.hpp +78 -0
  55. package/nitrogen/generated/android/c++/JFunc_void_std__vector_std__string_.hpp +14 -14
  56. package/nitrogen/generated/android/c++/JHybridRecognizerSpec.cpp +68 -19
  57. package/nitrogen/generated/android/c++/JHybridRecognizerSpec.hpp +7 -4
  58. package/nitrogen/generated/android/c++/JIosPreset.hpp +58 -0
  59. package/nitrogen/generated/android/c++/JMutableSpeechRecognitionConfig.hpp +79 -0
  60. package/nitrogen/generated/android/c++/{JSpeechToTextParams.hpp → JSpeechRecognitionConfig.hpp} +48 -30
  61. package/nitrogen/generated/android/c++/JVolumeChangeEvent.hpp +65 -0
  62. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/Func_void_VolumeChangeEvent.kt +80 -0
  63. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/HybridRecognizerSpec.kt +18 -5
  64. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/IosPreset.kt +23 -0
  65. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/MutableSpeechRecognitionConfig.kt +76 -0
  66. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/SpeechRecognitionConfig.kt +121 -0
  67. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/VolumeChangeEvent.kt +61 -0
  68. package/nitrogen/generated/ios/NitroSpeech-Swift-Cxx-Bridge.cpp +46 -30
  69. package/nitrogen/generated/ios/NitroSpeech-Swift-Cxx-Bridge.hpp +203 -70
  70. package/nitrogen/generated/ios/NitroSpeech-Swift-Cxx-Umbrella.hpp +13 -3
  71. package/nitrogen/generated/ios/c++/HybridRecognizerSpecSwift.hpp +41 -9
  72. package/nitrogen/generated/ios/swift/Func_void_VolumeChangeEvent.swift +46 -0
  73. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
  74. package/nitrogen/generated/ios/swift/HybridRecognizerSpec.swift +6 -3
  75. package/nitrogen/generated/ios/swift/HybridRecognizerSpec_cxx.swift +66 -18
  76. package/nitrogen/generated/ios/swift/IosPreset.swift +40 -0
  77. package/nitrogen/generated/ios/swift/MutableSpeechRecognitionConfig.swift +118 -0
  78. package/nitrogen/generated/ios/swift/{SpeechToTextParams.swift → SpeechRecognitionConfig.swift} +108 -43
  79. package/nitrogen/generated/ios/swift/VolumeChangeEvent.swift +52 -0
  80. package/nitrogen/generated/shared/c++/HybridRecognizerSpec.cpp +4 -1
  81. package/nitrogen/generated/shared/c++/HybridRecognizerSpec.hpp +17 -7
  82. package/nitrogen/generated/shared/c++/IosPreset.hpp +76 -0
  83. package/nitrogen/generated/shared/c++/MutableSpeechRecognitionConfig.hpp +105 -0
  84. package/nitrogen/generated/shared/c++/{SpeechToTextParams.hpp → SpeechRecognitionConfig.hpp} +39 -20
  85. package/nitrogen/generated/shared/c++/VolumeChangeEvent.hpp +91 -0
  86. package/package.json +15 -16
  87. package/src/NitroSpeech.ts +5 -0
  88. package/src/Recognizer/RecognizerRef.ts +23 -0
  89. package/src/Recognizer/SpeechRecognizer.ts +10 -0
  90. package/src/Recognizer/methods.ts +40 -0
  91. package/src/Recognizer/types.ts +33 -0
  92. package/src/Recognizer/useRecognizer.ts +85 -0
  93. package/src/Recognizer/useVoiceInputVolume.ts +65 -0
  94. package/src/index.ts +6 -182
  95. package/src/specs/NitroSpeech.nitro.ts +2 -163
  96. package/src/specs/Recognizer.nitro.ts +110 -0
  97. package/src/specs/SpeechRecognitionConfig.ts +167 -0
  98. package/src/specs/VolumeChangeEvent.ts +31 -0
  99. package/android/proguard-rules.pro +0 -1
  100. package/ios/AnylyzerTranscriber.swift +0 -331
  101. package/ios/AutoStopper.swift +0 -69
  102. package/ios/HapticImpact.swift +0 -32
  103. package/ios/LegacySpeechRecognizer.swift +0 -161
  104. package/lib/commonjs/index.js +0 -145
  105. package/lib/commonjs/index.js.map +0 -1
  106. package/lib/commonjs/package.json +0 -1
  107. package/lib/commonjs/specs/NitroSpeech.nitro.js +0 -6
  108. package/lib/commonjs/specs/NitroSpeech.nitro.js.map +0 -1
  109. package/lib/module/index.js +0 -138
  110. package/lib/module/index.js.map +0 -1
  111. package/lib/module/package.json +0 -1
  112. package/lib/module/specs/NitroSpeech.nitro.js +0 -4
  113. package/lib/module/specs/NitroSpeech.nitro.js.map +0 -1
  114. package/lib/tsconfig.tsbuildinfo +0 -1
  115. package/lib/typescript/index.d.ts +0 -50
  116. package/lib/typescript/index.d.ts.map +0 -1
  117. package/lib/typescript/specs/NitroSpeech.nitro.d.ts +0 -162
  118. package/lib/typescript/specs/NitroSpeech.nitro.d.ts.map +0 -1
  119. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/SpeechToTextParams.kt +0 -68
@@ -0,0 +1,147 @@
1
+ import Foundation
2
+
3
+ final class AutoStopper {
4
+ private static let defaultSilenceThresholdMs = 8000.0
5
+ private static let defaultProgressIntervalMs = 1000.0
6
+ private static let minProgressIntervalMs = 50.0
7
+
8
+ private let lg = Lg(prefix: "AutoStopper", disable: true)
9
+
10
+ private let queue = DispatchQueue(label: "com.margelo.nitrospeech.autostopper")
11
+
12
+ private var silenceThresholdMs: Double
13
+ private var progressIntervalMs: Double
14
+ private var timeLeftMs: Double
15
+ private var isStopped = false
16
+ private var didTimeout = false
17
+ private var timer: DispatchSourceTimer?
18
+
19
+ private let onProgress: (Double) -> Void
20
+ private let onTimeout: () -> Void
21
+
22
+ init(
23
+ silenceThresholdMs: Double?,
24
+ progressIntervalMs: Double?,
25
+ onProgress: @escaping (Double) -> Void,
26
+ onTimeout: @escaping () -> Void
27
+ ) {
28
+ let threshold = Self.clampMs(silenceThresholdMs ?? Self.defaultSilenceThresholdMs)
29
+ let interval = Self.clampMs(progressIntervalMs ?? Self.defaultProgressIntervalMs)
30
+ self.silenceThresholdMs = threshold
31
+ self.progressIntervalMs = interval
32
+ self.timeLeftMs = threshold
33
+ self.onProgress = onProgress
34
+ self.onTimeout = onTimeout
35
+ }
36
+
37
+ deinit {
38
+ queue.sync {
39
+ stopLocked()
40
+ timeLeftMs = 0
41
+ }
42
+ }
43
+
44
+ func resetTimer(from: String) {
45
+ queue.async { [weak self] in
46
+ guard let self, !self.isStopped else { return }
47
+ lg.log("[resetTimer] from:\(from)")
48
+ self.didTimeout = false
49
+ self.timeLeftMs = self.silenceThresholdMs
50
+ self.startOrRescheduleTimerLocked()
51
+ if self.timeLeftMs > 0 {
52
+ self.onProgress(self.timeLeftMs)
53
+ }
54
+ }
55
+ }
56
+
57
+ func updateThreshold(_ newThresholdMs: Double, from: String) {
58
+ queue.async { [weak self] in
59
+ guard let self, !self.isStopped else { return }
60
+ lg.log("[updateThreshold] from:\(from) newThresholdMs:\(newThresholdMs)")
61
+ self.silenceThresholdMs = Self.clampMs(newThresholdMs)
62
+ }
63
+ }
64
+
65
+ func addMsOnce(_ extraMs: Double, from: String) {
66
+ queue.async { [weak self] in
67
+ guard let self, !self.isStopped, extraMs.isFinite else { return }
68
+ lg.log("[addMsOnce] from:\(from) extraMs:\(extraMs)")
69
+ self.timeLeftMs += extraMs
70
+ self.didTimeout = false
71
+ if self.timeLeftMs > 0, self.timer != nil {
72
+ self.onProgress(self.timeLeftMs)
73
+ }
74
+ }
75
+ }
76
+
77
+ func updateProgressInterval(_ newIntervalMs: Double, from: String) {
78
+ queue.async { [weak self] in
79
+ guard let self, !self.isStopped else { return }
80
+ lg.log("[updateProgressInterval] from:\(from) newIntervalMs:\(newIntervalMs)")
81
+ self.progressIntervalMs = Self.clampMs(newIntervalMs)
82
+ if self.timer != nil {
83
+ self.startOrRescheduleTimerLocked()
84
+ }
85
+ }
86
+ }
87
+
88
+ func stop() {
89
+ queue.async { [weak self] in
90
+ guard let self else { return }
91
+ self.stopLocked()
92
+ self.timeLeftMs = 0
93
+ }
94
+ }
95
+
96
+ private func startOrRescheduleTimerLocked() {
97
+ timer?.cancel()
98
+ timer = nil
99
+
100
+ let source = DispatchSource.makeTimerSource(queue: queue)
101
+ let intervalNs = UInt64(progressIntervalMs * 1_000_000)
102
+ source.schedule(
103
+ deadline: .now() + .nanoseconds(Int(intervalNs)),
104
+ repeating: .nanoseconds(Int(intervalNs))
105
+ )
106
+ source.setEventHandler { [weak self] in
107
+ self?.tickLocked()
108
+ }
109
+ timer = source
110
+ source.resume()
111
+ }
112
+
113
+ private func tickLocked() {
114
+ guard !isStopped else { return }
115
+ guard !didTimeout else { return }
116
+
117
+ timeLeftMs -= progressIntervalMs
118
+ if timeLeftMs > 0 {
119
+ lg.log("[onProgress] timeLeftMs:\(timeLeftMs)")
120
+ onProgress(timeLeftMs)
121
+ return
122
+ }
123
+
124
+ timeLeftMs = 0
125
+ didTimeout = true
126
+ cancelTimerLocked()
127
+ lg.log("[onTimeout]")
128
+ onTimeout()
129
+ }
130
+
131
+ private func stopLocked() {
132
+ isStopped = true
133
+ cancelTimerLocked()
134
+ }
135
+
136
+ private func cancelTimerLocked() {
137
+ timer?.cancel()
138
+ timer = nil
139
+ }
140
+
141
+ private static func clampMs(_ value: Double) -> Double {
142
+ if !value.isFinite {
143
+ return minProgressIntervalMs
144
+ }
145
+ return max(minProgressIntervalMs, value)
146
+ }
147
+ }
@@ -0,0 +1,24 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ enum HapticImpact {
5
+ static func trigger(with: HapticFeedbackStyle?) {
6
+ // Default behavior - medium
7
+ let style = with ?? HapticFeedbackStyle.medium
8
+ let hapticStyle: UIImpactFeedbackGenerator.FeedbackStyle? = switch style {
9
+ case .light:
10
+ UIImpactFeedbackGenerator.FeedbackStyle.light
11
+ case .medium:
12
+ UIImpactFeedbackGenerator.FeedbackStyle.medium
13
+ case .heavy:
14
+ UIImpactFeedbackGenerator.FeedbackStyle.heavy
15
+ case .none:
16
+ nil
17
+ }
18
+ if let hapticStyle {
19
+ let impactGenerator = UIImpactFeedbackGenerator(style: hapticStyle)
20
+ impactGenerator.prepare()
21
+ impactGenerator.impactOccurred()
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,41 @@
1
+ import Foundation
2
+ import os.log
3
+
4
+ enum Log {
5
+ static let isLogging = false
6
+ static let subsystem = "com.margelo.nitro.nitrospeech"
7
+ static let category = "NitroSpeech"
8
+ static func log(_ text: String) {
9
+ if isLogging {
10
+ let tn = Thread.current.isMainThread ? "main" : "bg"
11
+ Logger(subsystem: subsystem, category: category).info(
12
+ "[thread]: \(tn) | \(text)"
13
+ )
14
+ }
15
+ }
16
+ }
17
+
18
+ final class Lg {
19
+ private let disable: Bool
20
+ static let isLogging = false
21
+ static let subsystem = "com.margelo.nitro.nitrospeech"
22
+ static let category = "NitroSpeech"
23
+ let prefix: String
24
+ init(prefix: String, disable: Bool? = false) {
25
+ self.prefix = prefix
26
+ self.disable = disable ?? false
27
+ }
28
+ var prevMs: Double?
29
+ func log(_ text: String) {
30
+ if Self.isLogging && !self.disable {
31
+ let nowMs = ProcessInfo.processInfo.systemUptime * 1000
32
+ let diff = Int(round(nowMs - (self.prevMs ?? nowMs)))
33
+ self.prevMs = nowMs
34
+ let tn = Thread.current.isMainThread ? "main" : "bg"
35
+ Logger(
36
+ subsystem: Self.subsystem,
37
+ category: Self.category
38
+ ).info("[thread]: \(tn) | {\(self.prefix)} diff: \(diff) | \(text)")
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,59 @@
1
+ import Foundation
2
+ import Speech
3
+ import AVFoundation
4
+
5
+ final class Permissions {
6
+ private let onGranted: () async -> Void
7
+ private let onDenied: (() -> Void)?
8
+ private let onError: ((String) -> Void)?
9
+
10
+ init(
11
+ onGranted: @escaping () async -> Void,
12
+ onDenied: (() -> Void)?,
13
+ onError: ((String) -> Void)?
14
+ ) {
15
+ self.onGranted = onGranted
16
+ self.onDenied = onDenied
17
+ self.onError = onError
18
+ }
19
+
20
+ private func requestMicrophonePermission() async {
21
+ // Request permission to record.
22
+ if #available(iOS 17.0, *) {
23
+ if await AVAudioApplication.requestRecordPermission() {
24
+ await self.onGranted()
25
+ return
26
+ }
27
+ self.onDenied?()
28
+ return
29
+ }
30
+ AVAudioSession.sharedInstance().requestRecordPermission { [weak self] granted in
31
+ Task { @MainActor in
32
+ guard let self else { return }
33
+ if granted {
34
+ await self.onGranted()
35
+ return
36
+ }
37
+ self.onDenied?()
38
+ }
39
+ }
40
+ }
41
+
42
+ func requestAuthorization() {
43
+ SFSpeechRecognizer.requestAuthorization { authStatus in
44
+ Task { @MainActor in
45
+ switch authStatus {
46
+ case .authorized:
47
+ await self.requestMicrophonePermission()
48
+ case .denied, .restricted:
49
+ self.onDenied?()
50
+ case .notDetermined:
51
+ self.onError?("Speech recognition not determined")
52
+ @unknown default:
53
+ self.onError?("Unknown authorization status")
54
+ }
55
+ }
56
+ }
57
+ }
58
+
59
+ }
@@ -0,0 +1,58 @@
1
+ import Foundation
2
+
3
+ enum Utils {
4
+ static func repeatingFilter(_ text: String) -> String {
5
+ var subStrings = text.split { $0.isWhitespace }.map { String($0) }
6
+ if #available(iOS 16.0, *) {
7
+ var shift = 0
8
+ while shift < subStrings.count, !subStrings[shift].contains(/\w+/) {
9
+ shift += 1
10
+ }
11
+ if shift > 0, shift < subStrings.count {
12
+ subStrings = Array(subStrings.suffix(subStrings.count - shift))
13
+ }
14
+ }
15
+ guard !subStrings.isEmpty else { return text }
16
+
17
+ var joiner = ""
18
+ if subStrings.count >= 10 {
19
+ joiner = subStrings.prefix(subStrings.count - 9).joined(separator: " ")
20
+ subStrings = Array(subStrings.suffix(10))
21
+ } else {
22
+ joiner = subStrings.first ?? ""
23
+ }
24
+ for i in subStrings.indices {
25
+ if i == 0 { continue }
26
+ if #available(iOS 16.0, *), subStrings[i].contains(/\d+/) {
27
+ joiner += " \(subStrings[i])"
28
+ continue
29
+ }
30
+ if subStrings[i] == subStrings[i - 1] { continue }
31
+ joiner += " \(subStrings[i])"
32
+ }
33
+ return joiner
34
+ }
35
+
36
+ // hash only params that affect transcriber preference
37
+ static func hashParams(_ params: SpeechRecognitionConfig?) -> String {
38
+ guard let params else { return "n" }
39
+ let locale = params.locale ?? "en-US"
40
+ let addPunctuation = switch params.iosAddPunctuation {
41
+ case nil: "n"
42
+ case false: "f"
43
+ case true: "t"
44
+ }
45
+ let preset = switch params.iosPreset {
46
+ case nil: "n"
47
+ case .shortform: "s"
48
+ case .general: "g"
49
+ }
50
+ let atypicalSpeech = switch params.iosAtypicalSpeech {
51
+ case nil: "n"
52
+ case false: "f"
53
+ case true: "t"
54
+ }
55
+
56
+ return [locale, addPunctuation, preset, atypicalSpeech].joined(separator: "|")
57
+ }
58
+ }
@@ -0,0 +1,2 @@
1
+ import type { NitroSpeech as NitroSpeechSpec } from './specs/NitroSpeech.nitro';
2
+ export declare const NitroSpeech: NitroSpeechSpec;
@@ -0,0 +1,2 @@
1
+ import { NitroModules } from 'react-native-nitro-modules';
2
+ export const NitroSpeech = NitroModules.createHybridObject('NitroSpeech');
@@ -0,0 +1,5 @@
1
+ import type { RecognizerMethods } from './types';
2
+ /**
3
+ * Safe cross-component reference to the Speech Recognizer methods.
4
+ */
5
+ export declare const RecognizerRef: RecognizerMethods;
@@ -0,0 +1,13 @@
1
+ import { recognizerAddAutoFinishTime, recognizerGetSupportedLocalesIOS, recognizerGetIsActive, recognizerResetAutoFinishTime, recognizerStartListening, recognizerStopListening, recognizerUpdateConfig, } from './methods';
2
+ /**
3
+ * Safe cross-component reference to the Speech Recognizer methods.
4
+ */
5
+ export const RecognizerRef = {
6
+ startListening: recognizerStartListening,
7
+ stopListening: recognizerStopListening,
8
+ resetAutoFinishTime: recognizerResetAutoFinishTime,
9
+ addAutoFinishTime: recognizerAddAutoFinishTime,
10
+ updateConfig: recognizerUpdateConfig,
11
+ getIsActive: recognizerGetIsActive,
12
+ getSupportedLocalesIOS: recognizerGetSupportedLocalesIOS,
13
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Static Speech Recognizer instance.
3
+ *
4
+ * Direct access to the all Speech Recognizer methods and callbacks.
5
+ *
6
+ * @note unsafe, might lead to race conditions
7
+ */
8
+ export declare const SpeechRecognizer: import("./types").RecognizerSpec;
@@ -0,0 +1,9 @@
1
+ import { NitroSpeech } from '../NitroSpeech';
2
+ /**
3
+ * Static Speech Recognizer instance.
4
+ *
5
+ * Direct access to the all Speech Recognizer methods and callbacks.
6
+ *
7
+ * @note unsafe, might lead to race conditions
8
+ */
9
+ export const SpeechRecognizer = NitroSpeech.recognizer;
@@ -0,0 +1,8 @@
1
+ import type { SpeechRecognitionConfig } from './types';
2
+ export declare const recognizerStartListening: (params: SpeechRecognitionConfig) => void;
3
+ export declare const recognizerStopListening: () => void;
4
+ export declare const recognizerResetAutoFinishTime: () => void;
5
+ export declare const recognizerAddAutoFinishTime: (additionalTimeMs?: number) => void;
6
+ export declare const recognizerUpdateConfig: (newConfig: SpeechRecognitionConfig, resetAutoFinishTime?: boolean) => void;
7
+ export declare const recognizerGetIsActive: () => boolean;
8
+ export declare const recognizerGetSupportedLocalesIOS: () => string[];
@@ -0,0 +1,29 @@
1
+ import { SpeechRecognizer } from './SpeechRecognizer';
2
+ export const recognizerStartListening = (params) => {
3
+ 'worklet';
4
+ SpeechRecognizer.startListening(params);
5
+ };
6
+ export const recognizerStopListening = () => {
7
+ 'worklet';
8
+ SpeechRecognizer.stopListening();
9
+ };
10
+ export const recognizerResetAutoFinishTime = () => {
11
+ 'worklet';
12
+ SpeechRecognizer.resetAutoFinishTime();
13
+ };
14
+ export const recognizerAddAutoFinishTime = (additionalTimeMs) => {
15
+ 'worklet';
16
+ SpeechRecognizer.addAutoFinishTime(additionalTimeMs);
17
+ };
18
+ export const recognizerUpdateConfig = (newConfig, resetAutoFinishTime) => {
19
+ 'worklet';
20
+ SpeechRecognizer.updateConfig(newConfig, resetAutoFinishTime);
21
+ };
22
+ export const recognizerGetIsActive = () => {
23
+ 'worklet';
24
+ return SpeechRecognizer.getIsActive();
25
+ };
26
+ export const recognizerGetSupportedLocalesIOS = () => {
27
+ 'worklet';
28
+ return SpeechRecognizer.getSupportedLocalesIOS().sort();
29
+ };
@@ -0,0 +1,6 @@
1
+ import type { Recognizer as RecognizerSpec } from '../specs/Recognizer.nitro';
2
+ import type { SpeechRecognitionConfig } from '../specs/SpeechRecognitionConfig';
3
+ import type { VolumeChangeEvent } from '../specs/VolumeChangeEvent';
4
+ type RecognizerCallbacks = Pick<RecognizerSpec, 'onReadyForSpeech' | 'onRecordingStopped' | 'onResult' | 'onAutoFinishProgress' | 'onError' | 'onPermissionDenied' | 'onVolumeChange'>;
5
+ type RecognizerMethods = Pick<RecognizerSpec, 'startListening' | 'stopListening' | 'resetAutoFinishTime' | 'addAutoFinishTime' | 'updateConfig' | 'getIsActive' | 'getSupportedLocalesIOS'>;
6
+ export type { RecognizerSpec, SpeechRecognitionConfig, VolumeChangeEvent, RecognizerCallbacks, RecognizerMethods, };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ import { type DependencyList } from 'react';
2
+ import type { RecognizerCallbacks, RecognizerMethods } from './types';
3
+ /**
4
+ * Safe, lifecycle-aware hook to use the recognizer.
5
+ *
6
+ * @param callbacks - The callbacks to use for the recognizer.
7
+ * @param destroyDeps - The additional dependencies to use for the cleanup effect.
8
+ *
9
+ * Example: To cleanup when the screen is unfocused.
10
+ *
11
+ * ```typescript
12
+ * const isFocused = useIsFocused()
13
+ * useRecognizer({ ... }, [isFocused])
14
+ * ```
15
+ */
16
+ export declare const useRecognizer: (callbacks: RecognizerCallbacks, destroyDeps?: DependencyList) => RecognizerMethods;
@@ -0,0 +1,71 @@
1
+ import { useEffect } from 'react';
2
+ import { recognizerResetAutoFinishTime, recognizerAddAutoFinishTime, recognizerUpdateConfig, recognizerGetIsActive, recognizerGetSupportedLocalesIOS, recognizerStartListening, recognizerStopListening, } from './methods';
3
+ import { SpeechRecognizer } from './SpeechRecognizer';
4
+ import { speechRecognizerVolumeChangeHandler } from './useVoiceInputVolume';
5
+ /**
6
+ * Safe, lifecycle-aware hook to use the recognizer.
7
+ *
8
+ * @param callbacks - The callbacks to use for the recognizer.
9
+ * @param destroyDeps - The additional dependencies to use for the cleanup effect.
10
+ *
11
+ * Example: To cleanup when the screen is unfocused.
12
+ *
13
+ * ```typescript
14
+ * const isFocused = useIsFocused()
15
+ * useRecognizer({ ... }, [isFocused])
16
+ * ```
17
+ */
18
+ export const useRecognizer = (callbacks, destroyDeps = []) => {
19
+ useEffect(() => {
20
+ if (callbacks.onVolumeChange) {
21
+ SpeechRecognizer.onVolumeChange = (event) => {
22
+ callbacks.onVolumeChange?.(event);
23
+ };
24
+ }
25
+ else {
26
+ SpeechRecognizer.onVolumeChange = speechRecognizerVolumeChangeHandler;
27
+ }
28
+ SpeechRecognizer.onReadyForSpeech = () => {
29
+ callbacks.onReadyForSpeech?.();
30
+ };
31
+ SpeechRecognizer.onRecordingStopped = () => {
32
+ callbacks.onRecordingStopped?.();
33
+ };
34
+ SpeechRecognizer.onResult = (resultBatches) => {
35
+ callbacks.onResult?.(resultBatches);
36
+ };
37
+ SpeechRecognizer.onAutoFinishProgress = (timeLeftMs) => {
38
+ callbacks.onAutoFinishProgress?.(timeLeftMs);
39
+ };
40
+ SpeechRecognizer.onError = (message) => {
41
+ callbacks.onError?.(message);
42
+ };
43
+ SpeechRecognizer.onPermissionDenied = () => {
44
+ callbacks.onPermissionDenied?.();
45
+ };
46
+ return () => {
47
+ SpeechRecognizer.onReadyForSpeech = undefined;
48
+ SpeechRecognizer.onRecordingStopped = undefined;
49
+ SpeechRecognizer.onResult = undefined;
50
+ SpeechRecognizer.onAutoFinishProgress = undefined;
51
+ SpeechRecognizer.onError = undefined;
52
+ SpeechRecognizer.onPermissionDenied = undefined;
53
+ SpeechRecognizer.onVolumeChange = undefined;
54
+ };
55
+ }, [callbacks]);
56
+ useEffect(() => {
57
+ return () => {
58
+ SpeechRecognizer.stopListening();
59
+ };
60
+ // eslint-disable-next-line react-hooks/exhaustive-deps
61
+ }, [...destroyDeps]);
62
+ return {
63
+ startListening: recognizerStartListening,
64
+ stopListening: recognizerStopListening,
65
+ resetAutoFinishTime: recognizerResetAutoFinishTime,
66
+ addAutoFinishTime: recognizerAddAutoFinishTime,
67
+ updateConfig: recognizerUpdateConfig,
68
+ getIsActive: recognizerGetIsActive,
69
+ getSupportedLocalesIOS: recognizerGetSupportedLocalesIOS,
70
+ };
71
+ };
@@ -0,0 +1,25 @@
1
+ import type { RecognizerSpec, VolumeChangeEvent } from './types';
2
+ type OnVolumeChange = RecognizerSpec['onVolumeChange'];
3
+ /**
4
+ * Subscription to the voice input volume changes
5
+ *
6
+ * Updates with arbitrary frequency (many times per second) while audio recording is active.
7
+ *
8
+ * @returns The current voice input volume normalized to a range of 0 to 1.
9
+ */
10
+ export declare const useVoiceInputVolume: () => VolumeChangeEvent;
11
+ /**
12
+ * Direct access to default Speech Recognizer volume change handler.
13
+ *
14
+ * In case you use static Speech Recognizer:
15
+ *
16
+ * ```typescript
17
+ * import { speechRecognizerVolumeChangeHandler } from '@gmessier/nitro-speech'
18
+ *
19
+ * SpeechRecognizer.onVolumeChange = speechRecognizerVolumeChangeHandler
20
+ * ... // setup everything else
21
+ * SpeechRecognizer.startListening({ locale: 'en-US' })
22
+ * ```
23
+ */
24
+ export declare const speechRecognizerVolumeChangeHandler: OnVolumeChange;
25
+ export {};
@@ -0,0 +1,52 @@
1
+ import { useSyncExternalStore } from 'react';
2
+ const subscribers = new Set();
3
+ let current = {
4
+ smoothedVolume: 0,
5
+ rawVolume: 0,
6
+ db: undefined,
7
+ };
8
+ let snapshot = { ...current };
9
+ const getSnapshot = () => {
10
+ if (snapshot.smoothedVolume === current.smoothedVolume &&
11
+ snapshot.rawVolume === current.rawVolume &&
12
+ snapshot.db === current.db) {
13
+ return snapshot;
14
+ }
15
+ snapshot = { ...current };
16
+ return snapshot;
17
+ };
18
+ /**
19
+ * Subscription to the voice input volume changes
20
+ *
21
+ * Updates with arbitrary frequency (many times per second) while audio recording is active.
22
+ *
23
+ * @returns The current voice input volume normalized to a range of 0 to 1.
24
+ */
25
+ export const useVoiceInputVolume = () => {
26
+ return useSyncExternalStore((subscriber) => {
27
+ subscribers.add(subscriber);
28
+ return () => subscribers.delete(subscriber);
29
+ }, getSnapshot);
30
+ };
31
+ /**
32
+ * Direct access to default Speech Recognizer volume change handler.
33
+ *
34
+ * In case you use static Speech Recognizer:
35
+ *
36
+ * ```typescript
37
+ * import { speechRecognizerVolumeChangeHandler } from '@gmessier/nitro-speech'
38
+ *
39
+ * SpeechRecognizer.onVolumeChange = speechRecognizerVolumeChangeHandler
40
+ * ... // setup everything else
41
+ * SpeechRecognizer.startListening({ locale: 'en-US' })
42
+ * ```
43
+ */
44
+ export const speechRecognizerVolumeChangeHandler = (event) => {
45
+ if (event.smoothedVolume === current.smoothedVolume &&
46
+ event.rawVolume === current.rawVolume &&
47
+ event.db === current.db) {
48
+ return;
49
+ }
50
+ current = event;
51
+ subscribers.forEach((subscriber) => subscriber?.(event));
52
+ };
package/lib/index.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export * from './Recognizer/useRecognizer';
2
+ export * from './Recognizer/useVoiceInputVolume';
3
+ export * from './Recognizer/SpeechRecognizer';
4
+ export * from './Recognizer/RecognizerRef';
5
+ export * from './Recognizer/types';
6
+ export * from './NitroSpeech';
package/lib/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export * from './Recognizer/useRecognizer';
2
+ export * from './Recognizer/useVoiceInputVolume';
3
+ export * from './Recognizer/SpeechRecognizer';
4
+ export * from './Recognizer/RecognizerRef';
5
+ export * from './Recognizer/types';
6
+ export * from './NitroSpeech';
@@ -0,0 +1,8 @@
1
+ import type { HybridObject } from 'react-native-nitro-modules';
2
+ import type { Recognizer } from './Recognizer.nitro';
3
+ export interface NitroSpeech extends HybridObject<{
4
+ ios: 'swift';
5
+ android: 'kotlin';
6
+ }> {
7
+ recognizer: Recognizer;
8
+ }
@@ -0,0 +1 @@
1
+ export {};