@gmessier/nitro-speech 0.3.3 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +176 -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 +82 -18
  7. package/android/src/main/java/com/margelo/nitro/nitrospeech/recognizer/HybridRecognizer.kt +118 -30
  8. package/android/src/main/java/com/margelo/nitro/nitrospeech/recognizer/Logger.kt +16 -0
  9. package/android/src/main/java/com/margelo/nitro/nitrospeech/recognizer/RecognitionListenerSession.kt +35 -24
  10. package/ios/{BufferUtil.swift → Audio/AudioBufferConverter.swift} +3 -34
  11. package/ios/Audio/AudioLevelTracker.swift +60 -0
  12. package/ios/Coordinator.swift +105 -0
  13. package/ios/Engines/AnalyzerEngine.swift +241 -0
  14. package/ios/Engines/DictationRuntime.swift +67 -0
  15. package/ios/Engines/RecognizerEngine.swift +315 -0
  16. package/ios/Engines/SFSpeechEngine.swift +119 -0
  17. package/ios/Engines/SpeechRuntime.swift +58 -0
  18. package/ios/Engines/TranscriberRuntimeProtocol.swift +21 -0
  19. package/ios/HybridNitroSpeech.swift +1 -10
  20. package/ios/HybridRecognizer.swift +142 -191
  21. package/ios/LocaleManager.swift +73 -0
  22. package/ios/{AppStateObserver.swift → Shared/AppStateObserver.swift} +1 -2
  23. package/ios/Shared/AutoStopper.swift +147 -0
  24. package/ios/Shared/HapticImpact.swift +24 -0
  25. package/ios/Shared/Log.swift +41 -0
  26. package/ios/Shared/Permissions.swift +59 -0
  27. package/ios/Shared/Utils.swift +58 -0
  28. package/lib/NitroSpeech.d.ts +2 -0
  29. package/lib/NitroSpeech.js +2 -0
  30. package/lib/Recognizer/RecognizerRef.d.ts +7 -0
  31. package/lib/Recognizer/RecognizerRef.js +16 -0
  32. package/lib/Recognizer/SpeechRecognizer.d.ts +8 -0
  33. package/lib/Recognizer/SpeechRecognizer.js +9 -0
  34. package/lib/Recognizer/methods.d.ts +9 -0
  35. package/lib/Recognizer/methods.js +33 -0
  36. package/lib/Recognizer/types.d.ts +6 -0
  37. package/lib/Recognizer/types.js +1 -0
  38. package/lib/Recognizer/useRecognizer.d.ts +16 -0
  39. package/lib/Recognizer/useRecognizer.js +71 -0
  40. package/lib/Recognizer/useRecognizerIsActive.d.ts +25 -0
  41. package/lib/Recognizer/useRecognizerIsActive.js +40 -0
  42. package/lib/Recognizer/useVoiceInputVolume.d.ts +25 -0
  43. package/lib/Recognizer/useVoiceInputVolume.js +52 -0
  44. package/lib/index.d.ts +7 -0
  45. package/lib/index.js +7 -0
  46. package/lib/specs/NitroSpeech.nitro.d.ts +8 -0
  47. package/lib/specs/NitroSpeech.nitro.js +1 -0
  48. package/lib/specs/Recognizer.nitro.d.ts +97 -0
  49. package/lib/specs/Recognizer.nitro.js +1 -0
  50. package/lib/specs/SpeechRecognitionConfig.d.ts +162 -0
  51. package/lib/specs/SpeechRecognitionConfig.js +1 -0
  52. package/lib/specs/VolumeChangeEvent.d.ts +31 -0
  53. package/lib/specs/VolumeChangeEvent.js +1 -0
  54. package/nitro.json +0 -4
  55. package/nitrogen/generated/android/NitroSpeech+autolinking.cmake +2 -2
  56. package/nitrogen/generated/android/NitroSpeechOnLoad.cpp +4 -2
  57. package/nitrogen/generated/android/c++/JFunc_void_VolumeChangeEvent.hpp +78 -0
  58. package/nitrogen/generated/android/c++/JFunc_void_std__vector_std__string_.hpp +14 -14
  59. package/nitrogen/generated/android/c++/JHybridRecognizerSpec.cpp +73 -19
  60. package/nitrogen/generated/android/c++/JHybridRecognizerSpec.hpp +8 -4
  61. package/nitrogen/generated/android/c++/JIosPreset.hpp +58 -0
  62. package/nitrogen/generated/android/c++/JMutableSpeechRecognitionConfig.hpp +79 -0
  63. package/nitrogen/generated/android/c++/{JSpeechToTextParams.hpp → JSpeechRecognitionConfig.hpp} +48 -30
  64. package/nitrogen/generated/android/c++/JVolumeChangeEvent.hpp +65 -0
  65. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/Func_void_VolumeChangeEvent.kt +80 -0
  66. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/HybridRecognizerSpec.kt +22 -5
  67. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/IosPreset.kt +23 -0
  68. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/MutableSpeechRecognitionConfig.kt +76 -0
  69. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/SpeechRecognitionConfig.kt +121 -0
  70. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/VolumeChangeEvent.kt +61 -0
  71. package/nitrogen/generated/ios/NitroSpeech-Swift-Cxx-Bridge.cpp +46 -30
  72. package/nitrogen/generated/ios/NitroSpeech-Swift-Cxx-Bridge.hpp +211 -69
  73. package/nitrogen/generated/ios/NitroSpeech-Swift-Cxx-Umbrella.hpp +13 -3
  74. package/nitrogen/generated/ios/c++/HybridRecognizerSpecSwift.hpp +49 -9
  75. package/nitrogen/generated/ios/swift/Func_void_VolumeChangeEvent.swift +46 -0
  76. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
  77. package/nitrogen/generated/ios/swift/HybridRecognizerSpec.swift +7 -3
  78. package/nitrogen/generated/ios/swift/HybridRecognizerSpec_cxx.swift +78 -18
  79. package/nitrogen/generated/ios/swift/IosPreset.swift +40 -0
  80. package/nitrogen/generated/ios/swift/MutableSpeechRecognitionConfig.swift +118 -0
  81. package/nitrogen/generated/ios/swift/{SpeechToTextParams.swift → SpeechRecognitionConfig.swift} +108 -43
  82. package/nitrogen/generated/ios/swift/VolumeChangeEvent.swift +52 -0
  83. package/nitrogen/generated/shared/c++/HybridRecognizerSpec.cpp +5 -1
  84. package/nitrogen/generated/shared/c++/HybridRecognizerSpec.hpp +18 -7
  85. package/nitrogen/generated/shared/c++/IosPreset.hpp +76 -0
  86. package/nitrogen/generated/shared/c++/MutableSpeechRecognitionConfig.hpp +105 -0
  87. package/nitrogen/generated/shared/c++/{SpeechToTextParams.hpp → SpeechRecognitionConfig.hpp} +39 -20
  88. package/nitrogen/generated/shared/c++/VolumeChangeEvent.hpp +91 -0
  89. package/package.json +15 -16
  90. package/src/NitroSpeech.ts +5 -0
  91. package/src/Recognizer/RecognizerRef.ts +27 -0
  92. package/src/Recognizer/SpeechRecognizer.ts +10 -0
  93. package/src/Recognizer/methods.ts +45 -0
  94. package/src/Recognizer/types.ts +34 -0
  95. package/src/Recognizer/useRecognizer.ts +87 -0
  96. package/src/Recognizer/useRecognizerIsActive.ts +49 -0
  97. package/src/Recognizer/useVoiceInputVolume.ts +65 -0
  98. package/src/index.ts +13 -182
  99. package/src/specs/NitroSpeech.nitro.ts +2 -163
  100. package/src/specs/Recognizer.nitro.ts +113 -0
  101. package/src/specs/SpeechRecognitionConfig.ts +167 -0
  102. package/src/specs/VolumeChangeEvent.ts +31 -0
  103. package/android/proguard-rules.pro +0 -1
  104. package/ios/AnylyzerTranscriber.swift +0 -331
  105. package/ios/AutoStopper.swift +0 -69
  106. package/ios/HapticImpact.swift +0 -32
  107. package/ios/LegacySpeechRecognizer.swift +0 -161
  108. package/lib/commonjs/index.js +0 -145
  109. package/lib/commonjs/index.js.map +0 -1
  110. package/lib/commonjs/package.json +0 -1
  111. package/lib/commonjs/specs/NitroSpeech.nitro.js +0 -6
  112. package/lib/commonjs/specs/NitroSpeech.nitro.js.map +0 -1
  113. package/lib/module/index.js +0 -138
  114. package/lib/module/index.js.map +0 -1
  115. package/lib/module/package.json +0 -1
  116. package/lib/module/specs/NitroSpeech.nitro.js +0 -4
  117. package/lib/module/specs/NitroSpeech.nitro.js.map +0 -1
  118. package/lib/tsconfig.tsbuildinfo +0 -1
  119. package/lib/typescript/index.d.ts +0 -50
  120. package/lib/typescript/index.d.ts.map +0 -1
  121. package/lib/typescript/specs/NitroSpeech.nitro.d.ts +0 -162
  122. package/lib/typescript/specs/NitroSpeech.nitro.d.ts.map +0 -1
  123. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/SpeechToTextParams.kt +0 -68
@@ -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,7 @@
1
+ import type { RecognizerMethods } from './types';
2
+ /**
3
+ * Safe cross-component reference to the Speech Recognizer methods.
4
+ *
5
+ * All methods support worklets and UI thread calls
6
+ */
7
+ export declare const RecognizerRef: RecognizerMethods;
@@ -0,0 +1,16 @@
1
+ import { recognizerAddAutoFinishTime, recognizerGetSupportedLocalesIOS, recognizerGetIsActive, recognizerResetAutoFinishTime, recognizerStartListening, recognizerStopListening, recognizerUpdateConfig, recognizerGetVoiceInputVolume, } from './methods';
2
+ /**
3
+ * Safe cross-component reference to the Speech Recognizer methods.
4
+ *
5
+ * All methods support worklets and UI thread calls
6
+ */
7
+ export const RecognizerRef = {
8
+ startListening: recognizerStartListening,
9
+ stopListening: recognizerStopListening,
10
+ resetAutoFinishTime: recognizerResetAutoFinishTime,
11
+ addAutoFinishTime: recognizerAddAutoFinishTime,
12
+ updateConfig: recognizerUpdateConfig,
13
+ getIsActive: recognizerGetIsActive,
14
+ getVoiceInputVolume: recognizerGetVoiceInputVolume,
15
+ getSupportedLocalesIOS: recognizerGetSupportedLocalesIOS,
16
+ };
@@ -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,9 @@
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 recognizerGetVoiceInputVolume: () => import("./types").VolumeChangeEvent;
9
+ export declare const recognizerGetSupportedLocalesIOS: () => string[];
@@ -0,0 +1,33 @@
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 recognizerGetVoiceInputVolume = () => {
27
+ 'worklet';
28
+ return SpeechRecognizer.getVoiceInputVolume();
29
+ };
30
+ export const recognizerGetSupportedLocalesIOS = () => {
31
+ 'worklet';
32
+ return SpeechRecognizer.getSupportedLocalesIOS().sort();
33
+ };
@@ -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' | 'getVoiceInputVolume' | '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, recognizerGetVoiceInputVolume, } from './methods';
3
+ import { SpeechRecognizer } from './SpeechRecognizer';
4
+ import { speechRecognizerVolumeChangeHandler } from './useVoiceInputVolume';
5
+ import { speechRecognizerActiveStateHandler } from './useRecognizerIsActive';
6
+ /**
7
+ * Safe, lifecycle-aware hook to use the recognizer.
8
+ *
9
+ * @param callbacks - The callbacks to use for the recognizer.
10
+ * @param destroyDeps - The additional dependencies to use for the cleanup effect.
11
+ *
12
+ * Example: To cleanup when the screen is unfocused.
13
+ *
14
+ * ```typescript
15
+ * const isFocused = useIsFocused()
16
+ * useRecognizer({ ... }, [isFocused])
17
+ * ```
18
+ */
19
+ export const useRecognizer = (callbacks, destroyDeps = []) => {
20
+ useEffect(() => {
21
+ SpeechRecognizer.onReadyForSpeech = () => {
22
+ speechRecognizerActiveStateHandler(true);
23
+ callbacks.onReadyForSpeech?.();
24
+ };
25
+ SpeechRecognizer.onRecordingStopped = () => {
26
+ speechRecognizerActiveStateHandler(false);
27
+ callbacks.onRecordingStopped?.();
28
+ };
29
+ SpeechRecognizer.onResult = (resultBatches) => {
30
+ callbacks.onResult?.(resultBatches);
31
+ };
32
+ SpeechRecognizer.onAutoFinishProgress = (timeLeftMs) => {
33
+ callbacks.onAutoFinishProgress?.(timeLeftMs);
34
+ };
35
+ SpeechRecognizer.onError = (message) => {
36
+ callbacks.onError?.(message);
37
+ };
38
+ SpeechRecognizer.onPermissionDenied = () => {
39
+ callbacks.onPermissionDenied?.();
40
+ };
41
+ SpeechRecognizer.onVolumeChange = (event) => {
42
+ speechRecognizerVolumeChangeHandler(event);
43
+ callbacks.onVolumeChange?.(event);
44
+ };
45
+ return () => {
46
+ SpeechRecognizer.onReadyForSpeech = undefined;
47
+ SpeechRecognizer.onRecordingStopped = undefined;
48
+ SpeechRecognizer.onResult = undefined;
49
+ SpeechRecognizer.onAutoFinishProgress = undefined;
50
+ SpeechRecognizer.onError = undefined;
51
+ SpeechRecognizer.onPermissionDenied = undefined;
52
+ SpeechRecognizer.onVolumeChange = undefined;
53
+ };
54
+ }, [callbacks]);
55
+ useEffect(() => {
56
+ return () => {
57
+ SpeechRecognizer.stopListening();
58
+ };
59
+ // eslint-disable-next-line react-hooks/exhaustive-deps
60
+ }, [...destroyDeps]);
61
+ return {
62
+ startListening: recognizerStartListening,
63
+ stopListening: recognizerStopListening,
64
+ resetAutoFinishTime: recognizerResetAutoFinishTime,
65
+ addAutoFinishTime: recognizerAddAutoFinishTime,
66
+ updateConfig: recognizerUpdateConfig,
67
+ getIsActive: recognizerGetIsActive,
68
+ getVoiceInputVolume: recognizerGetVoiceInputVolume,
69
+ getSupportedLocalesIOS: recognizerGetSupportedLocalesIOS,
70
+ };
71
+ };
@@ -0,0 +1,25 @@
1
+ type OnActiveStateChange = (isActive: boolean) => void;
2
+ /**
3
+ * Returns true if the speech recognition session is active.
4
+ */
5
+ export declare const useRecognizerIsActive: () => boolean;
6
+ /**
7
+ * Direct access to default Speech Recognizer isActive state change handler.
8
+ *
9
+ * In case you use static Speech Recognizer:
10
+ *
11
+ * ```typescript
12
+ * import { speechRecognizerActiveStateHandler } from '@gmessier/nitro-speech'
13
+ *
14
+ * SpeechRecognizer.onReadyForSpeech = () => {
15
+ * speechRecognizerActiveStateHandler(true)
16
+ * }
17
+ * SpeechRecognizer.onRecordingStopped = () => {
18
+ * speechRecognizerActiveStateHandler(false)
19
+ * }
20
+ * ... // setup everything else
21
+ * SpeechRecognizer.startListening({ locale: 'en-US' })
22
+ * ```
23
+ */
24
+ export declare const speechRecognizerActiveStateHandler: OnActiveStateChange;
25
+ export {};
@@ -0,0 +1,40 @@
1
+ import { useSyncExternalStore } from 'react';
2
+ const subscribers = new Set();
3
+ let recognizerIsActive = false;
4
+ const getSnapshot = () => {
5
+ return recognizerIsActive;
6
+ };
7
+ /**
8
+ * Returns true if the speech recognition session is active.
9
+ */
10
+ export const useRecognizerIsActive = () => {
11
+ return useSyncExternalStore((subscriber) => {
12
+ subscribers.add(subscriber);
13
+ return () => subscribers.delete(subscriber);
14
+ }, getSnapshot);
15
+ };
16
+ /**
17
+ * Direct access to default Speech Recognizer isActive state change handler.
18
+ *
19
+ * In case you use static Speech Recognizer:
20
+ *
21
+ * ```typescript
22
+ * import { speechRecognizerActiveStateHandler } from '@gmessier/nitro-speech'
23
+ *
24
+ * SpeechRecognizer.onReadyForSpeech = () => {
25
+ * speechRecognizerActiveStateHandler(true)
26
+ * }
27
+ * SpeechRecognizer.onRecordingStopped = () => {
28
+ * speechRecognizerActiveStateHandler(false)
29
+ * }
30
+ * ... // setup everything else
31
+ * SpeechRecognizer.startListening({ locale: 'en-US' })
32
+ * ```
33
+ */
34
+ export const speechRecognizerActiveStateHandler = (isActive) => {
35
+ if (isActive === recognizerIsActive) {
36
+ return;
37
+ }
38
+ recognizerIsActive = isActive;
39
+ subscribers.forEach((subscriber) => subscriber?.(isActive));
40
+ };
@@ -0,0 +1,25 @@
1
+ import type { RecognizerSpec, VolumeChangeEvent } from './types';
2
+ type OnVolumeChange = Exclude<RecognizerSpec['onVolumeChange'], undefined>;
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,7 @@
1
+ export * from './Recognizer/types';
2
+ export { useRecognizer } from './Recognizer/useRecognizer';
3
+ export { useVoiceInputVolume, speechRecognizerVolumeChangeHandler, } from './Recognizer/useVoiceInputVolume';
4
+ export { useRecognizerIsActive, speechRecognizerActiveStateHandler, } from './Recognizer/useRecognizerIsActive';
5
+ export { SpeechRecognizer } from './Recognizer/SpeechRecognizer';
6
+ export { RecognizerRef } from './Recognizer/RecognizerRef';
7
+ export { NitroSpeech } from './NitroSpeech';
package/lib/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from './Recognizer/types';
2
+ export { useRecognizer } from './Recognizer/useRecognizer';
3
+ export { useVoiceInputVolume, speechRecognizerVolumeChangeHandler, } from './Recognizer/useVoiceInputVolume';
4
+ export { useRecognizerIsActive, speechRecognizerActiveStateHandler, } from './Recognizer/useRecognizerIsActive';
5
+ export { SpeechRecognizer } from './Recognizer/SpeechRecognizer';
6
+ export { RecognizerRef } from './Recognizer/RecognizerRef';
7
+ export { NitroSpeech } 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 {};
@@ -0,0 +1,97 @@
1
+ import type { HybridObject } from 'react-native-nitro-modules';
2
+ import type { MutableSpeechRecognitionConfig, SpeechRecognitionConfig } from './SpeechRecognitionConfig';
3
+ import type { VolumeChangeEvent } from './VolumeChangeEvent';
4
+ export interface Recognizer extends HybridObject<{
5
+ ios: 'swift';
6
+ android: 'kotlin';
7
+ }> {
8
+ /**
9
+ * Prepare the speech recognition engine and the model for the given parameters.
10
+ *
11
+ * Omit the {@linkcode Promise} result if want to run synchronously.
12
+ * {@linkcode startListening} will start and resolve it automatically.
13
+ * Only `await` if run beforehand and want to react on the success
14
+ */
15
+ prewarm(defaultParams?: SpeechRecognitionConfig): Promise<void>;
16
+ /**
17
+ * Try to start the speech recognition.
18
+ *
19
+ * Not guaranteed to start the speech recognition.
20
+ *
21
+ * On success - {@linkcode onReadyForSpeech} is called
22
+ *
23
+ * On failure - {@linkcode onError} is called
24
+ */
25
+ startListening(params?: SpeechRecognitionConfig): void;
26
+ /**
27
+ * Stops the speech recognition. if not started, does nothing.
28
+ *
29
+ * Not a sync operation for android, delay about 250ms to polish the result.
30
+ *
31
+ * Use {@linkcode onRecordingStopped} to handle the stop event.
32
+ */
33
+ stopListening(): void;
34
+ /**
35
+ * Reset the auto finish timer to current {@linkcode SpeechRecognitionConfig.autoFinishRecognitionMs}.
36
+ */
37
+ resetAutoFinishTime(): void;
38
+ /**
39
+ * Add time to the auto finish timer once without changing the timer threshold.
40
+ *
41
+ * @param additionalTimeMs - time in ms to add to the current auto finish timer. If not set, will reset the timer to the original {@linkcode SpeechRecognitionConfig.autoFinishRecognitionMs}.
42
+ */
43
+ addAutoFinishTime(additionalTimeMs?: number): void;
44
+ /**
45
+ * Applies changes only within the active recognition session.
46
+ *
47
+ * @param newConfig - new dynamic params for the speech recognition.
48
+ * @param resetAutoFinishTime - if true, will reset auto finish time to actual {@linkcode SpeechRecognitionConfig.autoFinishRecognitionMs}.
49
+ */
50
+ updateConfig(newConfig?: MutableSpeechRecognitionConfig, resetAutoFinishTime?: boolean): void;
51
+ /**
52
+ * Returns true if the speech recognition is active.
53
+ */
54
+ getIsActive(): boolean;
55
+ /**
56
+ * Returns the current voice input volume.
57
+ */
58
+ getVoiceInputVolume(): VolumeChangeEvent;
59
+ /**
60
+ * Returns a list of supported locales.
61
+ *
62
+ * @platform iOS only
63
+ */
64
+ getSupportedLocalesIOS(): string[];
65
+ /**
66
+ * The speech recognition session has started.
67
+ */
68
+ onReadyForSpeech?: () => void;
69
+ /**
70
+ * The speech recognition session has stopped.
71
+ */
72
+ onRecordingStopped?: () => void;
73
+ /**
74
+ * Fires each time either a new batch has been added or the last batch has been updated.
75
+ */
76
+ onResult?: (resultBatches: string[]) => void;
77
+ /**
78
+ * Fires every {@linkcode SpeechRecognitionConfig.autoFinishProgressIntervalMs} or 1000ms
79
+ *
80
+ * Time left in milliseconds until the timer stops.
81
+ *
82
+ * @note not implemented for Android yet.
83
+ */
84
+ onAutoFinishProgress?: (timeLeftMs: number) => void;
85
+ /**
86
+ * Error of the speech recognition.
87
+ */
88
+ onError?: (message: string) => void;
89
+ /**
90
+ * The permission to use the microphone or recognize speech has been denied.
91
+ */
92
+ onPermissionDenied?: () => void;
93
+ /**
94
+ * Fires with high and arbitrary frequency (many times per second) while audio recording is active.
95
+ */
96
+ onVolumeChange?: (event: VolumeChangeEvent) => void;
97
+ }
@@ -0,0 +1 @@
1
+ export {};