@gmessier/nitro-speech 0.4.0 → 0.4.2

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 (42) hide show
  1. package/README.md +56 -16
  2. package/android/src/main/java/com/margelo/nitro/nitrospeech/recognizer/AutoStopper.kt +7 -7
  3. package/android/src/main/java/com/margelo/nitro/nitrospeech/recognizer/HybridRecognizer.kt +29 -14
  4. package/android/src/main/java/com/margelo/nitro/nitrospeech/recognizer/Logger.kt +16 -0
  5. package/android/src/main/java/com/margelo/nitro/nitrospeech/recognizer/RecognitionListenerSession.kt +11 -12
  6. package/ios/Audio/AudioLevelTracker.swift +16 -22
  7. package/ios/Engines/RecognizerEngine.swift +16 -13
  8. package/ios/HybridRecognizer.swift +8 -0
  9. package/ios/Shared/AutoStopper.swift +1 -1
  10. package/lib/Recognizer/RecognizerRef.d.ts +2 -0
  11. package/lib/Recognizer/RecognizerRef.js +5 -1
  12. package/lib/Recognizer/SpeechRecognizer.d.ts +2 -1
  13. package/lib/Recognizer/SpeechRecognizer.js +2 -1
  14. package/lib/Recognizer/methods.d.ts +5 -3
  15. package/lib/Recognizer/methods.js +8 -0
  16. package/lib/Recognizer/types.d.ts +3 -3
  17. package/lib/Recognizer/useRecognizer.js +10 -9
  18. package/lib/Recognizer/useRecognizerIsActive.d.ts +25 -0
  19. package/lib/Recognizer/useRecognizerIsActive.js +40 -0
  20. package/lib/Recognizer/useVoiceInputVolume.d.ts +1 -1
  21. package/lib/index.d.ts +7 -6
  22. package/lib/index.js +7 -6
  23. package/lib/specs/Recognizer.nitro.d.ts +26 -11
  24. package/nitrogen/generated/android/c++/JHybridRecognizerSpec.cpp +5 -0
  25. package/nitrogen/generated/android/c++/JHybridRecognizerSpec.hpp +1 -0
  26. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/HybridRecognizerSpec.kt +4 -0
  27. package/nitrogen/generated/ios/NitroSpeech-Swift-Cxx-Bridge.hpp +9 -0
  28. package/nitrogen/generated/ios/c++/HybridRecognizerSpecSwift.hpp +8 -0
  29. package/nitrogen/generated/ios/swift/HybridRecognizerSpec.swift +1 -0
  30. package/nitrogen/generated/ios/swift/HybridRecognizerSpec_cxx.swift +12 -0
  31. package/nitrogen/generated/shared/c++/HybridRecognizerSpec.cpp +1 -0
  32. package/nitrogen/generated/shared/c++/HybridRecognizerSpec.hpp +1 -0
  33. package/package.json +1 -1
  34. package/src/Recognizer/RecognizerRef.ts +6 -0
  35. package/src/Recognizer/SpeechRecognizer.ts +2 -1
  36. package/src/Recognizer/methods.ts +16 -3
  37. package/src/Recognizer/types.ts +7 -1
  38. package/src/Recognizer/useRecognizer.ts +11 -7
  39. package/src/Recognizer/useRecognizerIsActive.ts +49 -0
  40. package/src/Recognizer/useVoiceInputVolume.ts +1 -1
  41. package/src/index.ts +20 -6
  42. package/src/specs/Recognizer.nitro.ts +27 -11
@@ -1,8 +1,10 @@
1
- import type { SpeechRecognitionConfig } from './types';
2
- export declare const recognizerStartListening: (params: SpeechRecognitionConfig) => void;
1
+ import type { MutableSpeechRecognitionConfig, SpeechRecognitionConfig } from './types';
2
+ export declare const recognizerPrewarm: (params?: SpeechRecognitionConfig) => Promise<void>;
3
+ export declare const recognizerStartListening: (params?: SpeechRecognitionConfig) => void;
3
4
  export declare const recognizerStopListening: () => void;
4
5
  export declare const recognizerResetAutoFinishTime: () => void;
5
6
  export declare const recognizerAddAutoFinishTime: (additionalTimeMs?: number) => void;
6
- export declare const recognizerUpdateConfig: (newConfig: SpeechRecognitionConfig, resetAutoFinishTime?: boolean) => void;
7
+ export declare const recognizerUpdateConfig: (newConfig?: MutableSpeechRecognitionConfig, resetAutoFinishTime?: boolean) => void;
7
8
  export declare const recognizerGetIsActive: () => boolean;
9
+ export declare const recognizerGetVoiceInputVolume: () => import("./types").VolumeChangeEvent;
8
10
  export declare const recognizerGetSupportedLocalesIOS: () => string[];
@@ -1,4 +1,8 @@
1
1
  import { SpeechRecognizer } from './SpeechRecognizer';
2
+ export const recognizerPrewarm = (params) => {
3
+ 'worklet';
4
+ return SpeechRecognizer.prewarm(params);
5
+ };
2
6
  export const recognizerStartListening = (params) => {
3
7
  'worklet';
4
8
  SpeechRecognizer.startListening(params);
@@ -23,6 +27,10 @@ export const recognizerGetIsActive = () => {
23
27
  'worklet';
24
28
  return SpeechRecognizer.getIsActive();
25
29
  };
30
+ export const recognizerGetVoiceInputVolume = () => {
31
+ 'worklet';
32
+ return SpeechRecognizer.getVoiceInputVolume();
33
+ };
26
34
  export const recognizerGetSupportedLocalesIOS = () => {
27
35
  'worklet';
28
36
  return SpeechRecognizer.getSupportedLocalesIOS().sort();
@@ -1,6 +1,6 @@
1
1
  import type { Recognizer as RecognizerSpec } from '../specs/Recognizer.nitro';
2
- import type { SpeechRecognitionConfig } from '../specs/SpeechRecognitionConfig';
2
+ import type { MutableSpeechRecognitionConfig, SpeechRecognitionConfig } from '../specs/SpeechRecognitionConfig';
3
3
  import type { VolumeChangeEvent } from '../specs/VolumeChangeEvent';
4
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, };
5
+ type RecognizerMethods = Pick<RecognizerSpec, 'prewarm' | 'startListening' | 'stopListening' | 'resetAutoFinishTime' | 'addAutoFinishTime' | 'updateConfig' | 'getIsActive' | 'getVoiceInputVolume' | 'getSupportedLocalesIOS'>;
6
+ export type { RecognizerSpec, SpeechRecognitionConfig, MutableSpeechRecognitionConfig, VolumeChangeEvent, RecognizerCallbacks, RecognizerMethods, };
@@ -1,7 +1,8 @@
1
1
  import { useEffect } from 'react';
2
- import { recognizerResetAutoFinishTime, recognizerAddAutoFinishTime, recognizerUpdateConfig, recognizerGetIsActive, recognizerGetSupportedLocalesIOS, recognizerStartListening, recognizerStopListening, } from './methods';
2
+ import { recognizerResetAutoFinishTime, recognizerAddAutoFinishTime, recognizerUpdateConfig, recognizerGetIsActive, recognizerGetSupportedLocalesIOS, recognizerStartListening, recognizerStopListening, recognizerGetVoiceInputVolume, recognizerPrewarm, } from './methods';
3
3
  import { SpeechRecognizer } from './SpeechRecognizer';
4
4
  import { speechRecognizerVolumeChangeHandler } from './useVoiceInputVolume';
5
+ import { speechRecognizerActiveStateHandler } from './useRecognizerIsActive';
5
6
  /**
6
7
  * Safe, lifecycle-aware hook to use the recognizer.
7
8
  *
@@ -17,18 +18,12 @@ import { speechRecognizerVolumeChangeHandler } from './useVoiceInputVolume';
17
18
  */
18
19
  export const useRecognizer = (callbacks, destroyDeps = []) => {
19
20
  useEffect(() => {
20
- if (callbacks.onVolumeChange) {
21
- SpeechRecognizer.onVolumeChange = (event) => {
22
- callbacks.onVolumeChange?.(event);
23
- };
24
- }
25
- else {
26
- SpeechRecognizer.onVolumeChange = speechRecognizerVolumeChangeHandler;
27
- }
28
21
  SpeechRecognizer.onReadyForSpeech = () => {
22
+ speechRecognizerActiveStateHandler(true);
29
23
  callbacks.onReadyForSpeech?.();
30
24
  };
31
25
  SpeechRecognizer.onRecordingStopped = () => {
26
+ speechRecognizerActiveStateHandler(false);
32
27
  callbacks.onRecordingStopped?.();
33
28
  };
34
29
  SpeechRecognizer.onResult = (resultBatches) => {
@@ -43,6 +38,10 @@ export const useRecognizer = (callbacks, destroyDeps = []) => {
43
38
  SpeechRecognizer.onPermissionDenied = () => {
44
39
  callbacks.onPermissionDenied?.();
45
40
  };
41
+ SpeechRecognizer.onVolumeChange = (event) => {
42
+ speechRecognizerVolumeChangeHandler(event);
43
+ callbacks.onVolumeChange?.(event);
44
+ };
46
45
  return () => {
47
46
  SpeechRecognizer.onReadyForSpeech = undefined;
48
47
  SpeechRecognizer.onRecordingStopped = undefined;
@@ -60,12 +59,14 @@ export const useRecognizer = (callbacks, destroyDeps = []) => {
60
59
  // eslint-disable-next-line react-hooks/exhaustive-deps
61
60
  }, [...destroyDeps]);
62
61
  return {
62
+ prewarm: recognizerPrewarm,
63
63
  startListening: recognizerStartListening,
64
64
  stopListening: recognizerStopListening,
65
65
  resetAutoFinishTime: recognizerResetAutoFinishTime,
66
66
  addAutoFinishTime: recognizerAddAutoFinishTime,
67
67
  updateConfig: recognizerUpdateConfig,
68
68
  getIsActive: recognizerGetIsActive,
69
+ getVoiceInputVolume: recognizerGetVoiceInputVolume,
69
70
  getSupportedLocalesIOS: recognizerGetSupportedLocalesIOS,
70
71
  };
71
72
  };
@@ -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
+ };
@@ -1,5 +1,5 @@
1
1
  import type { RecognizerSpec, VolumeChangeEvent } from './types';
2
- type OnVolumeChange = RecognizerSpec['onVolumeChange'];
2
+ type OnVolumeChange = Exclude<RecognizerSpec['onVolumeChange'], undefined>;
3
3
  /**
4
4
  * Subscription to the voice input volume changes
5
5
  *
package/lib/index.d.ts CHANGED
@@ -1,6 +1,7 @@
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';
1
+ export { useRecognizer } from './Recognizer/useRecognizer';
2
+ export { useVoiceInputVolume, speechRecognizerVolumeChangeHandler, } from './Recognizer/useVoiceInputVolume';
3
+ export { useRecognizerIsActive, speechRecognizerActiveStateHandler, } from './Recognizer/useRecognizerIsActive';
4
+ export { SpeechRecognizer } from './Recognizer/SpeechRecognizer';
5
+ export { RecognizerRef } from './Recognizer/RecognizerRef';
6
+ export { NitroSpeech } from './NitroSpeech';
7
+ export { type RecognizerSpec, type SpeechRecognitionConfig, type MutableSpeechRecognitionConfig, type VolumeChangeEvent, type RecognizerCallbacks, type RecognizerMethods, } from './Recognizer/types';
package/lib/index.js CHANGED
@@ -1,6 +1,7 @@
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';
1
+ export { useRecognizer } from './Recognizer/useRecognizer';
2
+ export { useVoiceInputVolume, speechRecognizerVolumeChangeHandler, } from './Recognizer/useVoiceInputVolume';
3
+ export { useRecognizerIsActive, speechRecognizerActiveStateHandler, } from './Recognizer/useRecognizerIsActive';
4
+ export { SpeechRecognizer } from './Recognizer/SpeechRecognizer';
5
+ export { RecognizerRef } from './Recognizer/RecognizerRef';
6
+ export { NitroSpeech } from './NitroSpeech';
7
+ export {} from './Recognizer/types';
@@ -8,9 +8,24 @@ export interface Recognizer extends HybridObject<{
8
8
  /**
9
9
  * Prepare the speech recognition engine and the model for the given parameters.
10
10
  *
11
- * Omit the {@linkcode Promise} result if want to run synchronously.
11
+ * @notes
12
+ * - Not required, {@linkcode startListening} will prewarm automatically.
13
+ *
14
+ * - Omit the {@linkcode Promise} result if you want to run synchronously.
12
15
  * {@linkcode startListening} will start and resolve it automatically.
13
- * Only `await` if run beforehand and want to react on the success
16
+ *
17
+ * - Only `await` if run beforehand and you want to react to the success
18
+ *
19
+ * @worklet **Do not** use `await` on Worklet or UI runtimes. It will not work properly.
20
+ *
21
+ * You can run it synchronously:
22
+ * ```typescript
23
+ * scheduleOnRuntime(workletRuntime, () => {
24
+ * RecognizerRef.prewarm({
25
+ * // your config to prepare
26
+ * });
27
+ * });
28
+ * ```
14
29
  */
15
30
  prewarm(defaultParams?: SpeechRecognitionConfig): Promise<void>;
16
31
  /**
@@ -24,9 +39,9 @@ export interface Recognizer extends HybridObject<{
24
39
  */
25
40
  startListening(params?: SpeechRecognitionConfig): void;
26
41
  /**
27
- * Stops the speech recognition. if not started, does nothing.
42
+ * Stops the speech recognition. If not started, does nothing.
28
43
  *
29
- * Not a sync operation for android, delay about 250ms to polish the result.
44
+ * Not a sync operation for Android, delay about 250ms to polish the result.
30
45
  *
31
46
  * Use {@linkcode onRecordingStopped} to handle the stop event.
32
47
  */
@@ -52,6 +67,10 @@ export interface Recognizer extends HybridObject<{
52
67
  * Returns true if the speech recognition is active.
53
68
  */
54
69
  getIsActive(): boolean;
70
+ /**
71
+ * Returns the current voice input volume.
72
+ */
73
+ getVoiceInputVolume(): VolumeChangeEvent;
55
74
  /**
56
75
  * Returns a list of supported locales.
57
76
  *
@@ -67,15 +86,13 @@ export interface Recognizer extends HybridObject<{
67
86
  */
68
87
  onRecordingStopped?: () => void;
69
88
  /**
70
- * Called each time either a new batch has been added or the last batch has been updated.
89
+ * Fires each time either a new batch has been added or the last batch has been updated.
71
90
  */
72
91
  onResult?: (resultBatches: string[]) => void;
73
92
  /**
74
- * Called every {@linkcode SpeechRecognitionConfig.autoFinishProgressIntervalMs} or 1000ms
93
+ * Fires every {@linkcode SpeechRecognitionConfig.autoFinishProgressIntervalMs} or 1000ms
75
94
  *
76
95
  * Time left in milliseconds until the timer stops.
77
- *
78
- * @note not implemented for Android yet.
79
96
  */
80
97
  onAutoFinishProgress?: (timeLeftMs: number) => void;
81
98
  /**
@@ -87,9 +104,7 @@ export interface Recognizer extends HybridObject<{
87
104
  */
88
105
  onPermissionDenied?: () => void;
89
106
  /**
90
- * Called with high and arbitrary frequency (many times per second) while audio recording is active.
91
- *
92
- * @warning overriding it will disable the built-in `useVoiceInputVolume` hook.
107
+ * Fires with high and arbitrary frequency (many times per second) while audio recording is active.
93
108
  */
94
109
  onVolumeChange?: (event: VolumeChangeEvent) => void;
95
110
  }
@@ -233,6 +233,11 @@ namespace margelo::nitro::nitrospeech {
233
233
  auto __result = method(_javaPart);
234
234
  return static_cast<bool>(__result);
235
235
  }
236
+ VolumeChangeEvent JHybridRecognizerSpec::getVoiceInputVolume() {
237
+ static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JVolumeChangeEvent>()>("getVoiceInputVolume");
238
+ auto __result = method(_javaPart);
239
+ return __result->toCpp();
240
+ }
236
241
  std::vector<std::string> JHybridRecognizerSpec::getSupportedLocalesIOS() {
237
242
  static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<jni::JArrayClass<jni::JString>>()>("getSupportedLocalesIOS");
238
243
  auto __result = method(_javaPart);
@@ -74,6 +74,7 @@ namespace margelo::nitro::nitrospeech {
74
74
  void addAutoFinishTime(std::optional<double> additionalTimeMs) override;
75
75
  void updateConfig(const std::optional<MutableSpeechRecognitionConfig>& newConfig, std::optional<bool> resetAutoFinishTime) override;
76
76
  bool getIsActive() override;
77
+ VolumeChangeEvent getVoiceInputVolume() override;
77
78
  std::vector<std::string> getSupportedLocalesIOS() override;
78
79
 
79
80
  private:
@@ -153,6 +153,10 @@ abstract class HybridRecognizerSpec: HybridObject() {
153
153
  @Keep
154
154
  abstract fun getIsActive(): Boolean
155
155
 
156
+ @DoNotStrip
157
+ @Keep
158
+ abstract fun getVoiceInputVolume(): VolumeChangeEvent
159
+
156
160
  @DoNotStrip
157
161
  @Keep
158
162
  abstract fun getSupportedLocalesIOS(): Array<String>
@@ -454,6 +454,15 @@ namespace margelo::nitro::nitrospeech::bridge::swift {
454
454
  return Result<bool>::withError(error);
455
455
  }
456
456
 
457
+ // pragma MARK: Result<VolumeChangeEvent>
458
+ using Result_VolumeChangeEvent_ = Result<VolumeChangeEvent>;
459
+ inline Result_VolumeChangeEvent_ create_Result_VolumeChangeEvent_(const VolumeChangeEvent& value) noexcept {
460
+ return Result<VolumeChangeEvent>::withValue(value);
461
+ }
462
+ inline Result_VolumeChangeEvent_ create_Result_VolumeChangeEvent_(const std::exception_ptr& error) noexcept {
463
+ return Result<VolumeChangeEvent>::withError(error);
464
+ }
465
+
457
466
  // pragma MARK: Result<std::vector<std::string>>
458
467
  using Result_std__vector_std__string__ = Result<std::vector<std::string>>;
459
468
  inline Result_std__vector_std__string__ create_Result_std__vector_std__string__(const std::vector<std::string>& value) noexcept {
@@ -178,6 +178,14 @@ namespace margelo::nitro::nitrospeech {
178
178
  auto __value = std::move(__result.value());
179
179
  return __value;
180
180
  }
181
+ inline VolumeChangeEvent getVoiceInputVolume() override {
182
+ auto __result = _swiftPart.getVoiceInputVolume();
183
+ if (__result.hasError()) [[unlikely]] {
184
+ std::rethrow_exception(__result.error());
185
+ }
186
+ auto __value = std::move(__result.value());
187
+ return __value;
188
+ }
181
189
  inline std::vector<std::string> getSupportedLocalesIOS() override {
182
190
  auto __result = _swiftPart.getSupportedLocalesIOS();
183
191
  if (__result.hasError()) [[unlikely]] {
@@ -26,6 +26,7 @@ public protocol HybridRecognizerSpec_protocol: HybridObject {
26
26
  func addAutoFinishTime(additionalTimeMs: Double?) throws -> Void
27
27
  func updateConfig(newConfig: MutableSpeechRecognitionConfig?, resetAutoFinishTime: Bool?) throws -> Void
28
28
  func getIsActive() throws -> Bool
29
+ func getVoiceInputVolume() throws -> VolumeChangeEvent
29
30
  func getSupportedLocalesIOS() throws -> [String]
30
31
  }
31
32
 
@@ -452,6 +452,18 @@ open class HybridRecognizerSpec_cxx {
452
452
  }
453
453
  }
454
454
 
455
+ @inline(__always)
456
+ public final func getVoiceInputVolume() -> bridge.Result_VolumeChangeEvent_ {
457
+ do {
458
+ let __result = try self.__implementation.getVoiceInputVolume()
459
+ let __resultCpp = __result
460
+ return bridge.create_Result_VolumeChangeEvent_(__resultCpp)
461
+ } catch (let __error) {
462
+ let __exceptionPtr = __error.toCpp()
463
+ return bridge.create_Result_VolumeChangeEvent_(__exceptionPtr)
464
+ }
465
+ }
466
+
455
467
  @inline(__always)
456
468
  public final func getSupportedLocalesIOS() -> bridge.Result_std__vector_std__string__ {
457
469
  do {
@@ -35,6 +35,7 @@ namespace margelo::nitro::nitrospeech {
35
35
  prototype.registerHybridMethod("addAutoFinishTime", &HybridRecognizerSpec::addAutoFinishTime);
36
36
  prototype.registerHybridMethod("updateConfig", &HybridRecognizerSpec::updateConfig);
37
37
  prototype.registerHybridMethod("getIsActive", &HybridRecognizerSpec::getIsActive);
38
+ prototype.registerHybridMethod("getVoiceInputVolume", &HybridRecognizerSpec::getVoiceInputVolume);
38
39
  prototype.registerHybridMethod("getSupportedLocalesIOS", &HybridRecognizerSpec::getSupportedLocalesIOS);
39
40
  });
40
41
  }
@@ -80,6 +80,7 @@ namespace margelo::nitro::nitrospeech {
80
80
  virtual void addAutoFinishTime(std::optional<double> additionalTimeMs) = 0;
81
81
  virtual void updateConfig(const std::optional<MutableSpeechRecognitionConfig>& newConfig, std::optional<bool> resetAutoFinishTime) = 0;
82
82
  virtual bool getIsActive() = 0;
83
+ virtual VolumeChangeEvent getVoiceInputVolume() = 0;
83
84
  virtual std::vector<std::string> getSupportedLocalesIOS() = 0;
84
85
 
85
86
  protected:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gmessier/nitro-speech",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "React Native Real-time Speech Recognition Library powered by Nitro Modules",
5
5
  "main": "./lib/index.js",
6
6
  "module": "./lib/index.js",
@@ -7,17 +7,23 @@ import {
7
7
  recognizerStartListening,
8
8
  recognizerStopListening,
9
9
  recognizerUpdateConfig,
10
+ recognizerGetVoiceInputVolume,
11
+ recognizerPrewarm,
10
12
  } from './methods'
11
13
 
12
14
  /**
13
15
  * Safe cross-component reference to the Speech Recognizer methods.
16
+ *
17
+ * All methods support worklets and UI thread calls
14
18
  */
15
19
  export const RecognizerRef: RecognizerMethods = {
20
+ prewarm: recognizerPrewarm,
16
21
  startListening: recognizerStartListening,
17
22
  stopListening: recognizerStopListening,
18
23
  resetAutoFinishTime: recognizerResetAutoFinishTime,
19
24
  addAutoFinishTime: recognizerAddAutoFinishTime,
20
25
  updateConfig: recognizerUpdateConfig,
21
26
  getIsActive: recognizerGetIsActive,
27
+ getVoiceInputVolume: recognizerGetVoiceInputVolume,
22
28
  getSupportedLocalesIOS: recognizerGetSupportedLocalesIOS,
23
29
  }
@@ -5,6 +5,7 @@ import { NitroSpeech } from '../NitroSpeech'
5
5
  *
6
6
  * Direct access to the all Speech Recognizer methods and callbacks.
7
7
  *
8
- * @note unsafe, might lead to race conditions
8
+ * @note Unsafe, might lead to race conditions
9
+ * @warning Since it reflects the original hybrid object, its API may change in the future.
9
10
  */
10
11
  export const SpeechRecognizer = NitroSpeech.recognizer
@@ -1,7 +1,15 @@
1
1
  import { SpeechRecognizer } from './SpeechRecognizer'
2
- import type { SpeechRecognitionConfig } from './types'
2
+ import type {
3
+ MutableSpeechRecognitionConfig,
4
+ SpeechRecognitionConfig,
5
+ } from './types'
3
6
 
4
- export const recognizerStartListening = (params: SpeechRecognitionConfig) => {
7
+ export const recognizerPrewarm = (params?: SpeechRecognitionConfig) => {
8
+ 'worklet'
9
+ return SpeechRecognizer.prewarm(params)
10
+ }
11
+
12
+ export const recognizerStartListening = (params?: SpeechRecognitionConfig) => {
5
13
  'worklet'
6
14
  SpeechRecognizer.startListening(params)
7
15
  }
@@ -22,7 +30,7 @@ export const recognizerAddAutoFinishTime = (additionalTimeMs?: number) => {
22
30
  }
23
31
 
24
32
  export const recognizerUpdateConfig = (
25
- newConfig: SpeechRecognitionConfig,
33
+ newConfig?: MutableSpeechRecognitionConfig,
26
34
  resetAutoFinishTime?: boolean
27
35
  ) => {
28
36
  'worklet'
@@ -34,6 +42,11 @@ export const recognizerGetIsActive = () => {
34
42
  return SpeechRecognizer.getIsActive()
35
43
  }
36
44
 
45
+ export const recognizerGetVoiceInputVolume = () => {
46
+ 'worklet'
47
+ return SpeechRecognizer.getVoiceInputVolume()
48
+ }
49
+
37
50
  export const recognizerGetSupportedLocalesIOS = () => {
38
51
  'worklet'
39
52
  return SpeechRecognizer.getSupportedLocalesIOS().sort()
@@ -1,5 +1,8 @@
1
1
  import type { Recognizer as RecognizerSpec } from '../specs/Recognizer.nitro'
2
- import type { SpeechRecognitionConfig } from '../specs/SpeechRecognitionConfig'
2
+ import type {
3
+ MutableSpeechRecognitionConfig,
4
+ SpeechRecognitionConfig,
5
+ } from '../specs/SpeechRecognitionConfig'
3
6
  import type { VolumeChangeEvent } from '../specs/VolumeChangeEvent'
4
7
 
5
8
  type RecognizerCallbacks = Pick<
@@ -15,18 +18,21 @@ type RecognizerCallbacks = Pick<
15
18
 
16
19
  type RecognizerMethods = Pick<
17
20
  RecognizerSpec,
21
+ | 'prewarm'
18
22
  | 'startListening'
19
23
  | 'stopListening'
20
24
  | 'resetAutoFinishTime'
21
25
  | 'addAutoFinishTime'
22
26
  | 'updateConfig'
23
27
  | 'getIsActive'
28
+ | 'getVoiceInputVolume'
24
29
  | 'getSupportedLocalesIOS'
25
30
  >
26
31
 
27
32
  export type {
28
33
  RecognizerSpec,
29
34
  SpeechRecognitionConfig,
35
+ MutableSpeechRecognitionConfig,
30
36
  VolumeChangeEvent,
31
37
  RecognizerCallbacks,
32
38
  RecognizerMethods,
@@ -7,10 +7,13 @@ import {
7
7
  recognizerGetSupportedLocalesIOS,
8
8
  recognizerStartListening,
9
9
  recognizerStopListening,
10
+ recognizerGetVoiceInputVolume,
11
+ recognizerPrewarm,
10
12
  } from './methods'
11
13
  import type { RecognizerCallbacks, RecognizerMethods } from './types'
12
14
  import { SpeechRecognizer } from './SpeechRecognizer'
13
15
  import { speechRecognizerVolumeChangeHandler } from './useVoiceInputVolume'
16
+ import { speechRecognizerActiveStateHandler } from './useRecognizerIsActive'
14
17
 
15
18
  /**
16
19
  * Safe, lifecycle-aware hook to use the recognizer.
@@ -30,17 +33,12 @@ export const useRecognizer = (
30
33
  destroyDeps: DependencyList = []
31
34
  ): RecognizerMethods => {
32
35
  useEffect(() => {
33
- if (callbacks.onVolumeChange) {
34
- SpeechRecognizer.onVolumeChange = (event) => {
35
- callbacks.onVolumeChange?.(event)
36
- }
37
- } else {
38
- SpeechRecognizer.onVolumeChange = speechRecognizerVolumeChangeHandler
39
- }
40
36
  SpeechRecognizer.onReadyForSpeech = () => {
37
+ speechRecognizerActiveStateHandler(true)
41
38
  callbacks.onReadyForSpeech?.()
42
39
  }
43
40
  SpeechRecognizer.onRecordingStopped = () => {
41
+ speechRecognizerActiveStateHandler(false)
44
42
  callbacks.onRecordingStopped?.()
45
43
  }
46
44
  SpeechRecognizer.onResult = (resultBatches: string[]) => {
@@ -55,6 +53,10 @@ export const useRecognizer = (
55
53
  SpeechRecognizer.onPermissionDenied = () => {
56
54
  callbacks.onPermissionDenied?.()
57
55
  }
56
+ SpeechRecognizer.onVolumeChange = (event) => {
57
+ speechRecognizerVolumeChangeHandler(event)
58
+ callbacks.onVolumeChange?.(event)
59
+ }
58
60
  return () => {
59
61
  SpeechRecognizer.onReadyForSpeech = undefined
60
62
  SpeechRecognizer.onRecordingStopped = undefined
@@ -74,12 +76,14 @@ export const useRecognizer = (
74
76
  }, [...destroyDeps])
75
77
 
76
78
  return {
79
+ prewarm: recognizerPrewarm,
77
80
  startListening: recognizerStartListening,
78
81
  stopListening: recognizerStopListening,
79
82
  resetAutoFinishTime: recognizerResetAutoFinishTime,
80
83
  addAutoFinishTime: recognizerAddAutoFinishTime,
81
84
  updateConfig: recognizerUpdateConfig,
82
85
  getIsActive: recognizerGetIsActive,
86
+ getVoiceInputVolume: recognizerGetVoiceInputVolume,
83
87
  getSupportedLocalesIOS: recognizerGetSupportedLocalesIOS,
84
88
  }
85
89
  }
@@ -0,0 +1,49 @@
1
+ import { useSyncExternalStore } from 'react'
2
+
3
+ type OnActiveStateChange = (isActive: boolean) => void
4
+
5
+ const subscribers = new Set<OnActiveStateChange>()
6
+
7
+ let recognizerIsActive = false
8
+
9
+ const getSnapshot = () => {
10
+ return recognizerIsActive
11
+ }
12
+
13
+ /**
14
+ * Returns true if the speech recognition session is active.
15
+ */
16
+ export const useRecognizerIsActive = () => {
17
+ return useSyncExternalStore((subscriber) => {
18
+ subscribers.add(subscriber)
19
+ return () => subscribers.delete(subscriber)
20
+ }, getSnapshot)
21
+ }
22
+
23
+ /**
24
+ * Direct access to default Speech Recognizer isActive state change handler.
25
+ *
26
+ * In case you use static Speech Recognizer:
27
+ *
28
+ * ```typescript
29
+ * import { speechRecognizerActiveStateHandler } from '@gmessier/nitro-speech'
30
+ *
31
+ * SpeechRecognizer.onReadyForSpeech = () => {
32
+ * speechRecognizerActiveStateHandler(true)
33
+ * }
34
+ * SpeechRecognizer.onRecordingStopped = () => {
35
+ * speechRecognizerActiveStateHandler(false)
36
+ * }
37
+ * ... // setup everything else
38
+ * SpeechRecognizer.startListening({ locale: 'en-US' })
39
+ * ```
40
+ */
41
+ export const speechRecognizerActiveStateHandler: OnActiveStateChange = (
42
+ isActive
43
+ ) => {
44
+ if (isActive === recognizerIsActive) {
45
+ return
46
+ }
47
+ recognizerIsActive = isActive
48
+ subscribers.forEach((subscriber) => subscriber?.(isActive))
49
+ }
@@ -1,7 +1,7 @@
1
1
  import { useSyncExternalStore } from 'react'
2
2
  import type { RecognizerSpec, VolumeChangeEvent } from './types'
3
3
 
4
- type OnVolumeChange = RecognizerSpec['onVolumeChange']
4
+ type OnVolumeChange = Exclude<RecognizerSpec['onVolumeChange'], undefined>
5
5
 
6
6
  const subscribers = new Set<OnVolumeChange>()
7
7