@mentra/bluetooth-sdk 0.1.5 → 0.1.7
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/README.md +45 -27
- package/android/build.gradle +2 -9
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +53 -36
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +22 -9
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +25 -36
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +9 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +68 -56
- package/android/src/main/java/com/mentra/bluetoothsdk/audio/AudioModels.kt +125 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +209 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/connection/ConnectionModels.kt +78 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +4 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +2 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +72 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/internal/MapParsing.kt +132 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/requests/DisplayRequests.kt +39 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.java +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +2 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.java +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.java +168 -10
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +7 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +3 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +607 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/status/RuntimeState.kt +201 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/status/WifiHotspotStatus.kt +173 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +348 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +1 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +75 -0
- package/build/BluetoothSdk.types.d.ts +46 -20
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js +1 -0
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +6 -5
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +40 -5
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -2
- package/build/index.js.map +1 -1
- package/build/react/useMentraBluetooth.d.ts +5 -3
- package/build/react/useMentraBluetooth.d.ts.map +1 -1
- package/build/react/useMentraBluetooth.js +15 -4
- package/build/react/useMentraBluetooth.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +45 -28
- package/ios/MentraBluetoothSDK.podspec +0 -3
- package/ios/Source/Audio/AudioModels.swift +159 -0
- package/ios/Source/Bridge.swift +22 -11
- package/ios/Source/Camera/CameraModels.swift +248 -0
- package/ios/Source/Connection/ScanSession.swift +27 -0
- package/ios/Source/DeviceManager.swift +20 -97
- package/ios/Source/DeviceStore.swift +13 -4
- package/ios/Source/Errors/BluetoothError.swift +19 -0
- package/ios/Source/Events/BluetoothEvents.swift +149 -0
- package/ios/Source/Internal/BluetoothAvailability.swift +58 -0
- package/ios/Source/Internal/ValueParsing.swift +90 -0
- package/ios/Source/MentraBluetoothSDK.swift +50 -2150
- package/ios/Source/Requests/DisplayRequests.swift +58 -0
- package/ios/Source/Status/DeviceStatus.swift +471 -0
- package/ios/Source/Status/RuntimeState.swift +359 -0
- package/ios/Source/Status/WifiHotspotStatus.swift +326 -0
- package/ios/Source/Streaming/StreamModels.swift +436 -0
- package/ios/Source/Types/DeviceModels.swift +134 -0
- package/ios/Source/controllers/ControllerManager.swift +3 -3
- package/ios/Source/controllers/R1.swift +1 -1
- package/ios/Source/sgcs/G1.swift +1 -1
- package/ios/Source/sgcs/G2.swift +99 -39
- package/ios/Source/sgcs/Mach1.swift +1 -1
- package/ios/Source/sgcs/MentraLive.swift +109 -12
- package/ios/Source/sgcs/MentraNex.swift +1 -1
- package/ios/Source/sgcs/SGCManager.swift +11 -3
- package/ios/Source/sgcs/Simulated.swift +2 -2
- package/ios/Source/stt/SherpaOnnxTranscriber.swift +1 -4
- package/package.json +1 -1
- package/src/BluetoothSdk.types.ts +48 -28
- package/src/_private/BluetoothSdkModule.ts +47 -21
- package/src/index.ts +6 -4
- package/src/react/useMentraBluetooth.ts +19 -7
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothModels.kt +0 -1810
- package/android/src/main/java/com/mentra/bluetoothsdk/stt/VadGateSpeechPolicy.kt +0 -221
- package/ios/Packages/VAD/Common/VAD/VADQuality.swift +0 -41
- package/ios/Packages/VAD/Common/VAD/VADState.swift +0 -26
- package/ios/Packages/VAD/Common/VAD/VADStrategy.swift +0 -29
- package/ios/Packages/VAD/Common/VAD/VADType.swift +0 -14
- package/ios/Packages/VAD/Data/Configuration.swift +0 -68
- package/ios/Packages/VAD/Data/FrameSize.swift +0 -39
- package/ios/Packages/VAD/Data/Record.swift +0 -13
- package/ios/Packages/VAD/Data/Result.swift +0 -22
- package/ios/Packages/VAD/Data/SampleRate.swift +0 -48
- package/ios/Packages/VAD/Silero/Model/silero_vad.onnx +0 -0
- package/ios/Packages/VAD/Silero/SileroVAD.swift +0 -284
- package/ios/Packages/VAD/Silero/SileroVADStrategy.swift +0 -64
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// SileroVAD.swift
|
|
3
|
-
// ios-vad
|
|
4
|
-
//
|
|
5
|
-
// Created by baochuquan on 2024/11/9.
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
import Foundation
|
|
9
|
-
import onnxruntime_objc
|
|
10
|
-
|
|
11
|
-
public protocol SileroVADDelegate: AnyObject {
|
|
12
|
-
func sileroVADDidDetectSpeechStart()
|
|
13
|
-
func sileroVADDidDetectSpeechEnd()
|
|
14
|
-
func sileroVADDidDetectSpeeching()
|
|
15
|
-
func sileroVADDidDetectSilence()
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
public class SileroVAD: NSObject {
|
|
19
|
-
private enum State {
|
|
20
|
-
case silence
|
|
21
|
-
case start
|
|
22
|
-
case speeching
|
|
23
|
-
case end
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
private class InternalBuffer {
|
|
27
|
-
private let size: Int
|
|
28
|
-
// Use ContiguousArray for better memory layout and to avoid copy-on-write issues
|
|
29
|
-
// when the array buffer is accessed from multiple threads
|
|
30
|
-
private var buffer: ContiguousArray<Bool>
|
|
31
|
-
private let lock = NSLock()
|
|
32
|
-
|
|
33
|
-
init(size: Int) {
|
|
34
|
-
self.size = size
|
|
35
|
-
// Pre-allocate capacity to minimize reallocations
|
|
36
|
-
buffer = ContiguousArray<Bool>()
|
|
37
|
-
buffer.reserveCapacity(size + 1)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
func append(_ isSpeech: Bool) {
|
|
41
|
-
lock.lock()
|
|
42
|
-
// Perform all mutations while holding the lock
|
|
43
|
-
if buffer.count >= size {
|
|
44
|
-
// Remove oldest elements to make room
|
|
45
|
-
let removeCount = buffer.count - size + 1
|
|
46
|
-
buffer.removeFirst(removeCount)
|
|
47
|
-
}
|
|
48
|
-
buffer.append(isSpeech)
|
|
49
|
-
lock.unlock()
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
func isAllSpeech() -> Bool {
|
|
53
|
-
lock.lock()
|
|
54
|
-
// Copy the values we need while holding the lock
|
|
55
|
-
let count = buffer.count
|
|
56
|
-
let targetSize = size
|
|
57
|
-
let allTrue = count == targetSize && !buffer.contains(false)
|
|
58
|
-
lock.unlock()
|
|
59
|
-
return allTrue
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
func isAllNotSpeech() -> Bool {
|
|
63
|
-
lock.lock()
|
|
64
|
-
// Copy the values we need while holding the lock
|
|
65
|
-
let count = buffer.count
|
|
66
|
-
let targetSize = size
|
|
67
|
-
let allFalse = count == targetSize && !buffer.contains(true)
|
|
68
|
-
lock.unlock()
|
|
69
|
-
return allFalse
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// 支持两种采样率,不同的采样率时,支持的 windowSizeSmaple 不一样
|
|
74
|
-
// sample rate: 8000; sliceSize: 256/512/768
|
|
75
|
-
// sample rate: 16000; sliceSize: 512/1024/1536
|
|
76
|
-
|
|
77
|
-
static var modelPath: String {
|
|
78
|
-
// Try to find the model in the bundle containing this class (for CocoaPods)
|
|
79
|
-
let bundle = Bundle(for: SileroVAD.self)
|
|
80
|
-
if let path = bundle.path(forResource: "silero_vad", ofType: "onnx") {
|
|
81
|
-
return path
|
|
82
|
-
}
|
|
83
|
-
// Fall back to main bundle
|
|
84
|
-
return Bundle.main.path(forResource: "silero_vad", ofType: "onnx") ?? ""
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
public weak var delegate: SileroVADDelegate?
|
|
88
|
-
// 配置参数
|
|
89
|
-
private let sampleRate: Int64
|
|
90
|
-
private let sliceSizeSamples: Int64
|
|
91
|
-
private let threshold: Float
|
|
92
|
-
// 内部状态
|
|
93
|
-
private var state: State = .silence
|
|
94
|
-
private var silenceBuffer: InternalBuffer
|
|
95
|
-
private var speechBuffer: InternalBuffer
|
|
96
|
-
|
|
97
|
-
// 神经网络迭代状态
|
|
98
|
-
private var hidden: [[[Float]]]
|
|
99
|
-
private var cell: [[[Float]]]
|
|
100
|
-
private let hcSize: Int = 2 * 1 * 64
|
|
101
|
-
|
|
102
|
-
private var env: ORTEnv?
|
|
103
|
-
private var session: ORTSession
|
|
104
|
-
|
|
105
|
-
// Pre-allocated buffers to avoid creating new objects every frame (memory leak fix)
|
|
106
|
-
private var inputData: NSMutableData!
|
|
107
|
-
private var srData: NSMutableData!
|
|
108
|
-
private var hData: NSMutableData!
|
|
109
|
-
private var cData: NSMutableData!
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* sampleRate: 16000, 8000
|
|
113
|
-
* sliceSize:
|
|
114
|
-
* - sampleRate: 8000; sliceSize: 256, 512, 768
|
|
115
|
-
* - sampleRate: 16000; sliceSize: 512, 1024, 1536
|
|
116
|
-
*/
|
|
117
|
-
public init(sampleRate: Int64, sliceSize: Int64, threshold: Float, silenceTriggerDurationMs: Int64, speechTriggerDurationMs: Int64, modelPath: String = "") {
|
|
118
|
-
self.sampleRate = sampleRate
|
|
119
|
-
sliceSizeSamples = sliceSize
|
|
120
|
-
self.threshold = threshold
|
|
121
|
-
|
|
122
|
-
let samplesPerMs = sampleRate / 1000
|
|
123
|
-
let silenceBufferSize = Int(ceil(Float(samplesPerMs * silenceTriggerDurationMs) / Float(sliceSize)))
|
|
124
|
-
let speechBufferSize = Int(ceil(Float(samplesPerMs * speechTriggerDurationMs) / Float(sliceSize)))
|
|
125
|
-
silenceBuffer = InternalBuffer(size: silenceBufferSize)
|
|
126
|
-
speechBuffer = InternalBuffer(size: speechBufferSize)
|
|
127
|
-
|
|
128
|
-
hidden = Array(repeating: Array(repeating: Array(repeating: Float(0.0), count: 64), count: 1), count: 2)
|
|
129
|
-
cell = Array(repeating: Array(repeating: Array(repeating: Float(0.0), count: 64), count: 1), count: 2)
|
|
130
|
-
|
|
131
|
-
do {
|
|
132
|
-
env = try? ORTEnv(loggingLevel: .warning)
|
|
133
|
-
let sessionOptions = try? ORTSessionOptions()
|
|
134
|
-
try sessionOptions?.setIntraOpNumThreads(1)
|
|
135
|
-
try sessionOptions?.setGraphOptimizationLevel(.all)
|
|
136
|
-
let path: String
|
|
137
|
-
if modelPath.isEmpty {
|
|
138
|
-
path = Self.modelPath
|
|
139
|
-
} else {
|
|
140
|
-
path = modelPath
|
|
141
|
-
}
|
|
142
|
-
let session = try? ORTSession(env: env!, modelPath: path, sessionOptions: sessionOptions!)
|
|
143
|
-
self.session = session!
|
|
144
|
-
} catch {
|
|
145
|
-
fatalError()
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Pre-allocate reusable buffers for ONNX inference (prevents memory leak)
|
|
149
|
-
inputData = NSMutableData(length: Int(sliceSize) * MemoryLayout<Float>.size)!
|
|
150
|
-
srData = NSMutableData(bytes: [sampleRate], length: MemoryLayout<Int64>.size)
|
|
151
|
-
hData = NSMutableData(length: hcSize * MemoryLayout<Float>.size)!
|
|
152
|
-
cData = NSMutableData(length: hcSize * MemoryLayout<Float>.size)!
|
|
153
|
-
|
|
154
|
-
super.init()
|
|
155
|
-
debugLog("SampleRate = \(sampleRate); sliceSize = \(sliceSize); threshold = \(threshold); silenceTriggerDurationMs = \(silenceTriggerDurationMs); speechTriggerDurationMs = \(speechTriggerDurationMs)")
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
public func resetState() {
|
|
159
|
-
hidden = Array(repeating: Array(repeating: Array(repeating: Float(0.0), count: 64), count: 1), count: 2)
|
|
160
|
-
cell = Array(repeating: Array(repeating: Array(repeating: Float(0.0), count: 64), count: 1), count: 2)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
public func predict(data: [Float]) throws {
|
|
164
|
-
let inputShape: [NSNumber] = [1, NSNumber(value: sliceSizeSamples)]
|
|
165
|
-
|
|
166
|
-
// Copy input data into pre-allocated buffer (reuse buffer to avoid memory leak)
|
|
167
|
-
data.withUnsafeBytes { ptr in
|
|
168
|
-
inputData.replaceBytes(in: NSRange(location: 0, length: inputData.length), withBytes: ptr.baseAddress!)
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Copy hidden state into pre-allocated buffer
|
|
172
|
-
let flatHidden = hidden.flatMap { $0.flatMap { $0 } }
|
|
173
|
-
flatHidden.withUnsafeBytes { ptr in
|
|
174
|
-
hData.replaceBytes(in: NSRange(location: 0, length: hData.length), withBytes: ptr.baseAddress!)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Copy cell state into pre-allocated buffer
|
|
178
|
-
let flatCell = cell.flatMap { $0.flatMap { $0 } }
|
|
179
|
-
flatCell.withUnsafeBytes { ptr in
|
|
180
|
-
cData.replaceBytes(in: NSRange(location: 0, length: cData.length), withBytes: ptr.baseAddress!)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Create tensors from pre-allocated buffers (ORTValue creation is unavoidable but buffers are reused)
|
|
184
|
-
let inputTensor = try ORTValue(tensorData: inputData, elementType: .float, shape: inputShape)
|
|
185
|
-
let srTensor = try ORTValue(tensorData: srData, elementType: .int64, shape: [1])
|
|
186
|
-
let hTensor = try ORTValue(tensorData: hData, elementType: .float, shape: [2, 1, 64])
|
|
187
|
-
let cTensor = try ORTValue(tensorData: cData, elementType: .float, shape: [2, 1, 64])
|
|
188
|
-
|
|
189
|
-
let outputTensor = try session.run(
|
|
190
|
-
withInputs: ["input": inputTensor, "sr": srTensor, "h": hTensor, "c": cTensor],
|
|
191
|
-
outputNames: ["output", "hn", "cn"],
|
|
192
|
-
runOptions: nil
|
|
193
|
-
)
|
|
194
|
-
guard let outputValue = outputTensor["output"], let hiddenValue = outputTensor["hn"], let cellValue = outputTensor["cn"] else {
|
|
195
|
-
throw NSError(domain: "VadIterator", code: 1, userInfo: nil)
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
let outputData = try outputValue.tensorData() as Data
|
|
199
|
-
let probability = outputData.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) -> Float in
|
|
200
|
-
let floatBuffer = buffer.bindMemory(to: Float.self)
|
|
201
|
-
return floatBuffer[0]
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
let hc_shape = (2, 1, 64)
|
|
205
|
-
|
|
206
|
-
let hiddenData = try hiddenValue.tensorData() as Data
|
|
207
|
-
hiddenData.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in
|
|
208
|
-
let floatBuffer = buffer.bindMemory(to: Float.self)
|
|
209
|
-
for i in 0 ..< hc_shape.0 {
|
|
210
|
-
for j in 0 ..< hc_shape.1 {
|
|
211
|
-
for k in 0 ..< hc_shape.2 {
|
|
212
|
-
hidden[i][j][k] = floatBuffer[i * hc_shape.1 * hc_shape.2 + j * hc_shape.2 + k]
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
let cellData = try cellValue.tensorData() as Data
|
|
219
|
-
cellData.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in
|
|
220
|
-
let floatBuffer = buffer.bindMemory(to: Float.self)
|
|
221
|
-
for i in 0 ..< hc_shape.0 {
|
|
222
|
-
for j in 0 ..< hc_shape.1 {
|
|
223
|
-
for k in 0 ..< hc_shape.2 {
|
|
224
|
-
cell[i][j][k] = floatBuffer[i * hc_shape.1 * hc_shape.2 + j * hc_shape.2 + k]
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
let isSpeech = probability > threshold
|
|
231
|
-
if isSpeech {
|
|
232
|
-
debugLog("\(timestamp()) prob -> \(probability), true")
|
|
233
|
-
} else {
|
|
234
|
-
debugLog("\(timestamp()) prob -> \(probability)")
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// 缓存结果
|
|
238
|
-
silenceBuffer.append(isSpeech)
|
|
239
|
-
speechBuffer.append(isSpeech)
|
|
240
|
-
// 状态迁移
|
|
241
|
-
switch state {
|
|
242
|
-
case .silence:
|
|
243
|
-
if speechBuffer.isAllSpeech() {
|
|
244
|
-
state = .start
|
|
245
|
-
delegate?.sileroVADDidDetectSpeechStart()
|
|
246
|
-
state = .speeching
|
|
247
|
-
delegate?.sileroVADDidDetectSpeeching()
|
|
248
|
-
}
|
|
249
|
-
case .speeching:
|
|
250
|
-
if silenceBuffer.isAllNotSpeech() {
|
|
251
|
-
state = .end
|
|
252
|
-
delegate?.sileroVADDidDetectSpeechEnd()
|
|
253
|
-
state = .silence
|
|
254
|
-
delegate?.sileroVADDidDetectSilence()
|
|
255
|
-
}
|
|
256
|
-
default:
|
|
257
|
-
break
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
private func timestamp() -> String {
|
|
262
|
-
let date = Date()
|
|
263
|
-
let dateFormatter = DateFormatter()
|
|
264
|
-
dateFormatter.dateFormat = "HH:mm:ss.SSS"
|
|
265
|
-
return dateFormatter.string(from: date)
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
private func debugLog(_: String) {
|
|
269
|
-
#if DEBUG
|
|
270
|
-
// print("[Silero VAD]: " + content)
|
|
271
|
-
#endif
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
public extension Data {
|
|
276
|
-
/// 针对采样位数为 16 位的情况
|
|
277
|
-
func int16Array() -> [Int16] {
|
|
278
|
-
var array = [Int16](repeating: 0, count: count / MemoryLayout<Int16>.stride)
|
|
279
|
-
_ = array.withUnsafeMutableBytes {
|
|
280
|
-
self.copyBytes(to: $0, from: 0 ..< count)
|
|
281
|
-
}
|
|
282
|
-
return array
|
|
283
|
-
}
|
|
284
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// SileroVADStrategy.swift
|
|
3
|
-
// ios-vad
|
|
4
|
-
//
|
|
5
|
-
// Created by baochuquan on 2024/11/10.
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
import Foundation
|
|
9
|
-
|
|
10
|
-
// MARK: - SileroVADStrategy
|
|
11
|
-
|
|
12
|
-
class SileroVADStrategy: VADStrategy {
|
|
13
|
-
private var sileroVAD: SileroVAD?
|
|
14
|
-
private var state: VADState = .silence
|
|
15
|
-
private var handler: ((VADState) -> Void)?
|
|
16
|
-
|
|
17
|
-
func setup(sampleRate: SampleRate, frameSize: FrameSize, quality: VADQuality, silenceTriggerDurationMs: Int64, speechTriggerDurationMs: Int64) {
|
|
18
|
-
sileroVAD = SileroVAD(
|
|
19
|
-
sampleRate: Int64(sampleRate.rawValue),
|
|
20
|
-
sliceSize: Int64(frameSize.rawValue),
|
|
21
|
-
threshold: quality.threshold,
|
|
22
|
-
silenceTriggerDurationMs: silenceTriggerDurationMs,
|
|
23
|
-
speechTriggerDurationMs: speechTriggerDurationMs
|
|
24
|
-
)
|
|
25
|
-
sileroVAD?.delegate = self
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
func checkVAD(pcm: [Int16], handler: @escaping (VADState) -> Void) {
|
|
29
|
-
self.handler = handler
|
|
30
|
-
let data: [Float] = pcm.map { Float($0) / 32768.0 } // normalize
|
|
31
|
-
|
|
32
|
-
do {
|
|
33
|
-
try sileroVAD?.predict(data: data)
|
|
34
|
-
} catch _ {
|
|
35
|
-
fatalError()
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
func currentState() -> VADState {
|
|
40
|
-
return state
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
extension SileroVADStrategy: SileroVADDelegate {
|
|
45
|
-
func sileroVADDidDetectSpeechStart() {
|
|
46
|
-
state = .start
|
|
47
|
-
handler?(.start)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
func sileroVADDidDetectSpeechEnd() {
|
|
51
|
-
state = .end
|
|
52
|
-
handler?(.end)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
func sileroVADDidDetectSilence() {
|
|
56
|
-
state = .silence
|
|
57
|
-
handler?(.silence)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
func sileroVADDidDetectSpeeching() {
|
|
61
|
-
state = .speeching
|
|
62
|
-
handler?(.speeching)
|
|
63
|
-
}
|
|
64
|
-
}
|