@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.
- package/LICENSE +21 -0
- package/README.md +176 -148
- package/android/build.gradle +0 -1
- package/android/src/main/cpp/cpp-adapter.cpp +5 -1
- package/android/src/main/java/com/margelo/nitro/nitrospeech/HybridNitroSpeech.kt +2 -0
- package/android/src/main/java/com/margelo/nitro/nitrospeech/recognizer/AutoStopper.kt +82 -18
- package/android/src/main/java/com/margelo/nitro/nitrospeech/recognizer/HybridRecognizer.kt +118 -30
- package/android/src/main/java/com/margelo/nitro/nitrospeech/recognizer/Logger.kt +16 -0
- package/android/src/main/java/com/margelo/nitro/nitrospeech/recognizer/RecognitionListenerSession.kt +35 -24
- package/ios/{BufferUtil.swift → Audio/AudioBufferConverter.swift} +3 -34
- package/ios/Audio/AudioLevelTracker.swift +60 -0
- package/ios/Coordinator.swift +105 -0
- package/ios/Engines/AnalyzerEngine.swift +241 -0
- package/ios/Engines/DictationRuntime.swift +67 -0
- package/ios/Engines/RecognizerEngine.swift +315 -0
- package/ios/Engines/SFSpeechEngine.swift +119 -0
- package/ios/Engines/SpeechRuntime.swift +58 -0
- package/ios/Engines/TranscriberRuntimeProtocol.swift +21 -0
- package/ios/HybridNitroSpeech.swift +1 -10
- package/ios/HybridRecognizer.swift +142 -191
- package/ios/LocaleManager.swift +73 -0
- package/ios/{AppStateObserver.swift → Shared/AppStateObserver.swift} +1 -2
- package/ios/Shared/AutoStopper.swift +147 -0
- package/ios/Shared/HapticImpact.swift +24 -0
- package/ios/Shared/Log.swift +41 -0
- package/ios/Shared/Permissions.swift +59 -0
- package/ios/Shared/Utils.swift +58 -0
- package/lib/NitroSpeech.d.ts +2 -0
- package/lib/NitroSpeech.js +2 -0
- package/lib/Recognizer/RecognizerRef.d.ts +7 -0
- package/lib/Recognizer/RecognizerRef.js +16 -0
- package/lib/Recognizer/SpeechRecognizer.d.ts +8 -0
- package/lib/Recognizer/SpeechRecognizer.js +9 -0
- package/lib/Recognizer/methods.d.ts +9 -0
- package/lib/Recognizer/methods.js +33 -0
- package/lib/Recognizer/types.d.ts +6 -0
- package/lib/Recognizer/types.js +1 -0
- package/lib/Recognizer/useRecognizer.d.ts +16 -0
- package/lib/Recognizer/useRecognizer.js +71 -0
- package/lib/Recognizer/useRecognizerIsActive.d.ts +25 -0
- package/lib/Recognizer/useRecognizerIsActive.js +40 -0
- package/lib/Recognizer/useVoiceInputVolume.d.ts +25 -0
- package/lib/Recognizer/useVoiceInputVolume.js +52 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +7 -0
- package/lib/specs/NitroSpeech.nitro.d.ts +8 -0
- package/lib/specs/NitroSpeech.nitro.js +1 -0
- package/lib/specs/Recognizer.nitro.d.ts +97 -0
- package/lib/specs/Recognizer.nitro.js +1 -0
- package/lib/specs/SpeechRecognitionConfig.d.ts +162 -0
- package/lib/specs/SpeechRecognitionConfig.js +1 -0
- package/lib/specs/VolumeChangeEvent.d.ts +31 -0
- package/lib/specs/VolumeChangeEvent.js +1 -0
- package/nitro.json +0 -4
- package/nitrogen/generated/android/NitroSpeech+autolinking.cmake +2 -2
- package/nitrogen/generated/android/NitroSpeechOnLoad.cpp +4 -2
- package/nitrogen/generated/android/c++/JFunc_void_VolumeChangeEvent.hpp +78 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_std__string_.hpp +14 -14
- package/nitrogen/generated/android/c++/JHybridRecognizerSpec.cpp +73 -19
- package/nitrogen/generated/android/c++/JHybridRecognizerSpec.hpp +8 -4
- package/nitrogen/generated/android/c++/JIosPreset.hpp +58 -0
- package/nitrogen/generated/android/c++/JMutableSpeechRecognitionConfig.hpp +79 -0
- package/nitrogen/generated/android/c++/{JSpeechToTextParams.hpp → JSpeechRecognitionConfig.hpp} +48 -30
- package/nitrogen/generated/android/c++/JVolumeChangeEvent.hpp +65 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/Func_void_VolumeChangeEvent.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/HybridRecognizerSpec.kt +22 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/IosPreset.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/MutableSpeechRecognitionConfig.kt +76 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/SpeechRecognitionConfig.kt +121 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrospeech/VolumeChangeEvent.kt +61 -0
- package/nitrogen/generated/ios/NitroSpeech-Swift-Cxx-Bridge.cpp +46 -30
- package/nitrogen/generated/ios/NitroSpeech-Swift-Cxx-Bridge.hpp +211 -69
- package/nitrogen/generated/ios/NitroSpeech-Swift-Cxx-Umbrella.hpp +13 -3
- package/nitrogen/generated/ios/c++/HybridRecognizerSpecSwift.hpp +49 -9
- package/nitrogen/generated/ios/swift/Func_void_VolumeChangeEvent.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridRecognizerSpec.swift +7 -3
- package/nitrogen/generated/ios/swift/HybridRecognizerSpec_cxx.swift +78 -18
- package/nitrogen/generated/ios/swift/IosPreset.swift +40 -0
- package/nitrogen/generated/ios/swift/MutableSpeechRecognitionConfig.swift +118 -0
- package/nitrogen/generated/ios/swift/{SpeechToTextParams.swift → SpeechRecognitionConfig.swift} +108 -43
- package/nitrogen/generated/ios/swift/VolumeChangeEvent.swift +52 -0
- package/nitrogen/generated/shared/c++/HybridRecognizerSpec.cpp +5 -1
- package/nitrogen/generated/shared/c++/HybridRecognizerSpec.hpp +18 -7
- package/nitrogen/generated/shared/c++/IosPreset.hpp +76 -0
- package/nitrogen/generated/shared/c++/MutableSpeechRecognitionConfig.hpp +105 -0
- package/nitrogen/generated/shared/c++/{SpeechToTextParams.hpp → SpeechRecognitionConfig.hpp} +39 -20
- package/nitrogen/generated/shared/c++/VolumeChangeEvent.hpp +91 -0
- package/package.json +15 -16
- package/src/NitroSpeech.ts +5 -0
- package/src/Recognizer/RecognizerRef.ts +27 -0
- package/src/Recognizer/SpeechRecognizer.ts +10 -0
- package/src/Recognizer/methods.ts +45 -0
- package/src/Recognizer/types.ts +34 -0
- package/src/Recognizer/useRecognizer.ts +87 -0
- package/src/Recognizer/useRecognizerIsActive.ts +49 -0
- package/src/Recognizer/useVoiceInputVolume.ts +65 -0
- package/src/index.ts +13 -182
- package/src/specs/NitroSpeech.nitro.ts +2 -163
- package/src/specs/Recognizer.nitro.ts +113 -0
- package/src/specs/SpeechRecognitionConfig.ts +167 -0
- package/src/specs/VolumeChangeEvent.ts +31 -0
- package/android/proguard-rules.pro +0 -1
- package/ios/AnylyzerTranscriber.swift +0 -331
- package/ios/AutoStopper.swift +0 -69
- package/ios/HapticImpact.swift +0 -32
- package/ios/LegacySpeechRecognizer.swift +0 -161
- package/lib/commonjs/index.js +0 -145
- package/lib/commonjs/index.js.map +0 -1
- package/lib/commonjs/package.json +0 -1
- package/lib/commonjs/specs/NitroSpeech.nitro.js +0 -6
- package/lib/commonjs/specs/NitroSpeech.nitro.js.map +0 -1
- package/lib/module/index.js +0 -138
- package/lib/module/index.js.map +0 -1
- package/lib/module/package.json +0 -1
- package/lib/module/specs/NitroSpeech.nitro.js +0 -4
- package/lib/module/specs/NitroSpeech.nitro.js.map +0 -1
- package/lib/tsconfig.tsbuildinfo +0 -1
- package/lib/typescript/index.d.ts +0 -50
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/specs/NitroSpeech.nitro.d.ts +0 -162
- package/lib/typescript/specs/NitroSpeech.nitro.d.ts.map +0 -1
- 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,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,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 @@
|
|
|
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 {};
|