@mentra/bluetooth-sdk 0.1.6 → 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 +16 -14
- package/android/build.gradle +2 -9
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +40 -31
- 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 +8 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +34 -10
- package/android/src/main/java/com/mentra/bluetoothsdk/audio/AudioModels.kt +6 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +29 -7
- 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 +12 -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 +14 -12
- package/android/src/main/java/com/mentra/bluetoothsdk/status/RuntimeState.kt +4 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +3 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +1 -3
- package/build/BluetoothSdk.types.d.ts +42 -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 +37 -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 +41 -24
- package/ios/MentraBluetoothSDK.podspec +0 -3
- package/ios/Source/Audio/AudioModels.swift +10 -10
- package/ios/Source/Bridge.swift +22 -11
- package/ios/Source/Camera/CameraModels.swift +13 -11
- package/ios/Source/DeviceManager.swift +20 -97
- package/ios/Source/DeviceStore.swift +12 -4
- package/ios/Source/Events/BluetoothEvents.swift +34 -0
- package/ios/Source/MentraBluetoothSDK.swift +25 -10
- package/ios/Source/Status/DeviceStatus.swift +12 -4
- package/ios/Source/Status/RuntimeState.swift +22 -13
- package/ios/Source/Streaming/StreamModels.swift +5 -5
- 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 +2 -3
- 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 +44 -28
- package/src/_private/BluetoothSdkModule.ts +44 -21
- package/src/index.ts +6 -4
- package/src/react/useMentraBluetooth.ts +19 -7
- 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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMentraBluetooth.js","sourceRoot":"","sources":["../../src/react/useMentraBluetooth.ts"],"names":[],"mappings":"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","sourcesContent":["import {useState} from \"react\"\n\nimport BluetoothSdk from \"../index\"\nimport {\n DeviceModels,\n createDisconnectedGlassesStatus,\n isConnectedGlassesConnectionStatus,\n isReadyGlassesConnectionStatus,\n} from \"../BluetoothSdk.types\"\nimport type {\n ConnectOptions,\n Device,\n DeviceModel,\n GalleryMode,\n GlassesConnectionStatus,\n HotspotStatus,\n PublicBluetoothStatus,\n PublicGlassesStatus,\n MicMode,\n WifiSearchResult,\n WifiStatus,\n} from \"../BluetoothSdk.types\"\n\nimport {\n useGlassesConnection,\n type DefaultDeviceStorage,\n type GlassesConnectionHookResult,\n} from \"./useGlassesConnection\"\n\nexport type BatteryState = {\n charging: boolean\n level: number | null\n}\n\nexport type ConnectedGlassesInfo = {\n appVersion?: string\n bluetoothName?: string\n buildNumber?: string\n color?: string\n deviceModel?: string\n firmwareVersion?: string\n serialNumber?: string\n style?: string\n}\n\nexport type FirmwareInfo = {\n appVersion?: string\n buildNumber?: string\n source:\n | \"app\"\n | \"bes\"\n | \"device\"\n | \"firmware\"\n | \"left\"\n | \"mtk\"\n | \"right\"\n | \"unknown\"\n version: string | null\n}\n\nexport type SignalState = {\n strengthDbm: number | null\n updatedAt: number | null\n}\n\nexport type GlassesRuntimeState =\n | {\n connected: false\n connection: Exclude<GlassesConnectionStatus, {state: \"connected\"}>\n ready: false\n }\n | {\n battery: BatteryState\n connected: true\n connection: Extract<GlassesConnectionStatus, {state: \"connected\"}>\n device: ConnectedGlassesInfo\n firmware: FirmwareInfo\n hotspot: HotspotStatus\n ready: boolean\n signal: SignalState\n wifi: WifiStatus\n }\n\nexport type GalleryModeState = {\n applying: boolean\n desired: GalleryMode\n error: unknown | null\n}\n\nexport type PhoneSdkRuntimeState = {\n currentMic: MicMode | null\n defaultDevice: Device | null\n galleryMode: GalleryModeState\n lastLog: string[]\n micRanking: MicMode[]\n otherBluetoothConnected: boolean\n searching: boolean\n searchingController: boolean\n systemMicUnavailable: boolean\n wifiScanResults: WifiSearchResult[]\n}\n\nexport type ScanController = {\n active: boolean\n clear: () => void\n devices: Device[]\n error: unknown | null\n model: DeviceModel\n selectedDevice: Device | null\n selectDevice: (device: Device | null) => void\n setModel: (model: DeviceModel) => void\n start: (model?: DeviceModel) => Promise<Device[]>\n stop: () => Promise<void>\n}\n\nexport type UseMentraBluetoothOptions = {\n autoConnectDefault?: boolean\n defaultDeviceStorage?: DefaultDeviceStorage\n defaultModel?: DeviceModel\n onError?: (error: unknown) => void\n scanTimeoutMs?: number\n}\n\nexport type MentraBluetoothSession = {\n busy: boolean\n clearDefaultDevice: () => Promise<void>\n connect: (device?: Device, options?: ConnectOptions) => Promise<void>\n connectDefault: (options?: ConnectOptions) => Promise<void>\n defaultDevice: Device | null\n disconnect: () => Promise<void>\n error: unknown | null\n glasses: GlassesRuntimeState\n refresh: () => Promise<void>\n scan: ScanController\n sdk: PhoneSdkRuntimeState\n setDefaultDevice: (device: Device | null) => Promise<void>\n setGalleryMode: (mode: GalleryMode) => Promise<void>\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined\n}\n\nfunction numberValue(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null\n}\n\nfunction firmwareInfo(status: Partial<PublicGlassesStatus>): FirmwareInfo {\n const firmwareSources = [\n [\"firmwareVersion\", \"firmware\"],\n [\"deviceFirmwareVersion\", \"device\"],\n [\"rightFirmwareVersion\", \"right\"],\n [\"leftFirmwareVersion\", \"left\"],\n [\"besFirmwareVersion\", \"bes\"],\n [\"mtkFirmwareVersion\", \"mtk\"],\n [\"appVersion\", \"app\"],\n ] as const\n\n for (const [key, source] of firmwareSources) {\n const value = stringValue((status as Record<string, unknown>)[key])\n if (value) {\n return {\n appVersion: stringValue(status.appVersion),\n buildNumber: stringValue(status.buildNumber),\n source,\n version: value,\n }\n }\n }\n\n return {\n appVersion: stringValue(status.appVersion),\n buildNumber: stringValue(status.buildNumber),\n source: \"unknown\",\n version: null,\n }\n}\n\nfunction batteryState(status: Partial<PublicGlassesStatus>): BatteryState {\n const level = typeof status.batteryLevel === \"number\" && status.batteryLevel >= 0 ? status.batteryLevel : null\n return {\n charging: status.charging ?? false,\n level,\n }\n}\n\nfunction connectedGlassesInfo(status: Partial<PublicGlassesStatus>): ConnectedGlassesInfo {\n return {\n appVersion: stringValue(status.appVersion),\n bluetoothName: stringValue(status.bluetoothName),\n buildNumber: stringValue(status.buildNumber),\n color: stringValue(status.color),\n deviceModel: stringValue(status.deviceModel),\n firmwareVersion: stringValue(status.firmwareVersion),\n serialNumber: stringValue(status.serialNumber),\n style: stringValue(status.style),\n }\n}\n\nfunction runtimeGlassesState(status: Partial<PublicGlassesStatus>): GlassesRuntimeState {\n const connection = status.connection ?? createDisconnectedGlassesStatus().connection ?? {state: \"disconnected\"}\n\n if (!isConnectedGlassesConnectionStatus(connection)) {\n return {\n connected: false,\n connection,\n ready: false,\n }\n }\n\n return {\n battery: batteryState(status),\n connected: true,\n connection,\n device: connectedGlassesInfo(status),\n firmware: firmwareInfo(status),\n hotspot: status.hotspot ?? {state: \"disabled\"},\n ready: isReadyGlassesConnectionStatus(connection),\n signal: {\n strengthDbm: numberValue((status as Record<string, unknown>).signalStrength),\n updatedAt: numberValue((status as Record<string, unknown>).signalStrengthUpdatedAt),\n },\n wifi: status.wifi ?? {state: \"disconnected\"},\n }\n}\n\nfunction phoneSdkState(\n status: Partial<PublicBluetoothStatus>,\n defaultDevice: Device | null,\n galleryMode: GalleryModeState,\n): PhoneSdkRuntimeState {\n return {\n currentMic: status.currentMic || null,\n defaultDevice,\n galleryMode,\n lastLog: status.lastLog ?? [],\n micRanking: status.micRanking ?? [],\n otherBluetoothConnected: status.otherBtConnected ?? false,\n searching: status.searching ?? false,\n searchingController: status.searchingController ?? false,\n systemMicUnavailable: status.systemMicUnavailable ?? false,\n wifiScanResults: status.wifiScanResults ?? [],\n }\n}\n\nfunction scanController(connection: GlassesConnectionHookResult): ScanController {\n return {\n active: connection.scan.scanning,\n clear: connection.scan.clearResults,\n devices: connection.scan.devices,\n error: connection.scan.error,\n model: connection.scan.model,\n selectedDevice: connection.scan.selectedDevice,\n selectDevice: connection.scan.selectDevice,\n setModel: connection.scan.setModel,\n start: connection.scan.startScan,\n stop: connection.scan.stopScan,\n }\n}\n\nexport function useMentraBluetooth(options: UseMentraBluetoothOptions = {}): MentraBluetoothSession {\n const connection = useGlassesConnection({\n autoConnectDefault: options.autoConnectDefault,\n defaultDeviceStorage: options.defaultDeviceStorage,\n onError: options.onError,\n scanModel: options.defaultModel ?? DeviceModels.MentraLive,\n scanTimeoutMs: options.scanTimeoutMs,\n })\n const [galleryModeApplying, setGalleryModeApplying] = useState(false)\n const [galleryModeError, setGalleryModeError] = useState<unknown | null>(null)\n\n async function setGalleryMode(mode: GalleryMode) {\n setGalleryModeApplying(true)\n setGalleryModeError(null)\n try {\n await BluetoothSdk.setGalleryMode(mode)\n } catch (error) {\n setGalleryModeError(error)\n options.onError?.(error)\n throw error\n } finally {\n setGalleryModeApplying(false)\n }\n }\n\n const galleryMode: GalleryModeState = {\n applying: galleryModeApplying,\n desired: connection.bluetoothStatus.galleryModeAuto === false ? \"manual\" : \"auto\",\n error: galleryModeError,\n }\n\n return {\n busy: connection.busy || galleryModeApplying,\n clearDefaultDevice: connection.clearDefaultDevice,\n connect: connection.connect,\n connectDefault: connection.connectDefault,\n defaultDevice: connection.defaultDevice,\n disconnect: connection.disconnect,\n error: galleryModeError ?? connection.error,\n glasses: runtimeGlassesState(connection.glassesStatus),\n refresh: connection.refresh,\n scan: scanController(connection),\n sdk: phoneSdkState(connection.bluetoothStatus, connection.defaultDevice, galleryMode),\n setDefaultDevice: connection.setDefaultDevice,\n setGalleryMode,\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"useMentraBluetooth.js","sourceRoot":"","sources":["../../src/react/useMentraBluetooth.ts"],"names":[],"mappings":"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","sourcesContent":["import {useState} from \"react\"\n\nimport BluetoothSdk from \"../index\"\nimport {\n DeviceModels,\n createDisconnectedGlassesStatus,\n isConnectedGlassesConnectionStatus,\n isReadyGlassesConnectionStatus,\n} from \"../BluetoothSdk.types\"\nimport type {\n ConnectOptions,\n Device,\n DeviceModel,\n GlassesConnectionStatus,\n HotspotStatus,\n PublicBluetoothStatus,\n PublicGlassesStatus,\n MicMode,\n WifiSearchResult,\n WifiStatus,\n} from \"../BluetoothSdk.types\"\n\nimport {\n useGlassesConnection,\n type DefaultDeviceStorage,\n type GlassesConnectionHookResult,\n} from \"./useGlassesConnection\"\n\nexport type BatteryState = {\n charging: boolean\n level: number | null\n}\n\nexport type ConnectedGlassesInfo = {\n appVersion?: string\n bluetoothName?: string\n buildNumber?: string\n color?: string\n deviceModel?: string\n firmwareVersion?: string\n serialNumber?: string\n style?: string\n}\n\nexport type FirmwareInfo = {\n appVersion?: string\n buildNumber?: string\n source:\n | \"app\"\n | \"bes\"\n | \"device\"\n | \"firmware\"\n | \"left\"\n | \"mtk\"\n | \"right\"\n | \"unknown\"\n version: string | null\n}\n\nexport type SignalState = {\n strengthDbm: number | null\n updatedAt: number | null\n}\n\nexport type GlassesRuntimeState =\n | {\n connected: false\n connection: Exclude<GlassesConnectionStatus, {state: \"connected\"}>\n ready: false\n }\n | {\n battery: BatteryState\n connected: true\n connection: Extract<GlassesConnectionStatus, {state: \"connected\"}>\n device: ConnectedGlassesInfo\n firmware: FirmwareInfo\n hotspot: HotspotStatus\n ready: boolean\n signal: SignalState\n voiceActivityDetectionEnabled: boolean\n wifi: WifiStatus\n }\n\nexport type GalleryModeState = {\n applying: boolean\n enabled: boolean\n error: unknown | null\n}\n\nexport type PhoneSdkRuntimeState = {\n currentMic: MicMode | null\n defaultDevice: Device | null\n galleryMode: GalleryModeState\n lastLog: string[]\n micRanking: MicMode[]\n otherBluetoothConnected: boolean\n searching: boolean\n searchingController: boolean\n systemMicUnavailable: boolean\n wifiScanResults: WifiSearchResult[]\n}\n\nexport type ScanController = {\n active: boolean\n clear: () => void\n devices: Device[]\n error: unknown | null\n model: DeviceModel\n selectedDevice: Device | null\n selectDevice: (device: Device | null) => void\n setModel: (model: DeviceModel) => void\n start: (model?: DeviceModel) => Promise<Device[]>\n stop: () => Promise<void>\n}\n\nexport type UseMentraBluetoothOptions = {\n autoConnectDefault?: boolean\n defaultDeviceStorage?: DefaultDeviceStorage\n defaultModel?: DeviceModel\n onError?: (error: unknown) => void\n scanTimeoutMs?: number\n}\n\nexport type MentraBluetoothSession = {\n busy: boolean\n clearDefaultDevice: () => Promise<void>\n connect: (device?: Device, options?: ConnectOptions) => Promise<void>\n connectDefault: (options?: ConnectOptions) => Promise<void>\n defaultDevice: Device | null\n disconnect: () => Promise<void>\n error: unknown | null\n glasses: GlassesRuntimeState\n refresh: () => Promise<void>\n scan: ScanController\n sdk: PhoneSdkRuntimeState\n setDefaultDevice: (device: Device | null) => Promise<void>\n setGalleryModeEnabled: (enabled: boolean) => Promise<void>\n setVoiceActivityDetectionEnabled: (enabled: boolean) => Promise<void>\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined\n}\n\nfunction numberValue(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null\n}\n\nfunction firmwareInfo(status: Partial<PublicGlassesStatus>): FirmwareInfo {\n const firmwareSources = [\n [\"firmwareVersion\", \"firmware\"],\n [\"deviceFirmwareVersion\", \"device\"],\n [\"rightFirmwareVersion\", \"right\"],\n [\"leftFirmwareVersion\", \"left\"],\n [\"besFirmwareVersion\", \"bes\"],\n [\"mtkFirmwareVersion\", \"mtk\"],\n [\"appVersion\", \"app\"],\n ] as const\n\n for (const [key, source] of firmwareSources) {\n const value = stringValue((status as Record<string, unknown>)[key])\n if (value) {\n return {\n appVersion: stringValue(status.appVersion),\n buildNumber: stringValue(status.buildNumber),\n source,\n version: value,\n }\n }\n }\n\n return {\n appVersion: stringValue(status.appVersion),\n buildNumber: stringValue(status.buildNumber),\n source: \"unknown\",\n version: null,\n }\n}\n\nfunction batteryState(status: Partial<PublicGlassesStatus>): BatteryState {\n const level = typeof status.batteryLevel === \"number\" && status.batteryLevel >= 0 ? status.batteryLevel : null\n return {\n charging: status.charging ?? false,\n level,\n }\n}\n\nfunction connectedGlassesInfo(status: Partial<PublicGlassesStatus>): ConnectedGlassesInfo {\n return {\n appVersion: stringValue(status.appVersion),\n bluetoothName: stringValue(status.bluetoothName),\n buildNumber: stringValue(status.buildNumber),\n color: stringValue(status.color),\n deviceModel: stringValue(status.deviceModel),\n firmwareVersion: stringValue(status.firmwareVersion),\n serialNumber: stringValue(status.serialNumber),\n style: stringValue(status.style),\n }\n}\n\nfunction runtimeGlassesState(status: Partial<PublicGlassesStatus>): GlassesRuntimeState {\n const connection = status.connection ?? createDisconnectedGlassesStatus().connection ?? {state: \"disconnected\"}\n\n if (!isConnectedGlassesConnectionStatus(connection)) {\n return {\n connected: false,\n connection,\n ready: false,\n }\n }\n\n return {\n battery: batteryState(status),\n connected: true,\n connection,\n device: connectedGlassesInfo(status),\n firmware: firmwareInfo(status),\n hotspot: status.hotspot ?? {state: \"disabled\"},\n ready: isReadyGlassesConnectionStatus(connection),\n signal: {\n strengthDbm: numberValue((status as Record<string, unknown>).signalStrength),\n updatedAt: numberValue((status as Record<string, unknown>).signalStrengthUpdatedAt),\n },\n voiceActivityDetectionEnabled: status.voiceActivityDetectionEnabled ?? true,\n wifi: status.wifi ?? {state: \"disconnected\"},\n }\n}\n\nfunction phoneSdkState(\n status: Partial<PublicBluetoothStatus>,\n defaultDevice: Device | null,\n galleryMode: GalleryModeState,\n): PhoneSdkRuntimeState {\n return {\n currentMic: status.currentMic || null,\n defaultDevice,\n galleryMode,\n lastLog: status.lastLog ?? [],\n micRanking: status.micRanking ?? [],\n otherBluetoothConnected: status.otherBtConnected ?? false,\n searching: status.searching ?? false,\n searchingController: status.searchingController ?? false,\n systemMicUnavailable: status.systemMicUnavailable ?? false,\n wifiScanResults: status.wifiScanResults ?? [],\n }\n}\n\nfunction scanController(connection: GlassesConnectionHookResult): ScanController {\n return {\n active: connection.scan.scanning,\n clear: connection.scan.clearResults,\n devices: connection.scan.devices,\n error: connection.scan.error,\n model: connection.scan.model,\n selectedDevice: connection.scan.selectedDevice,\n selectDevice: connection.scan.selectDevice,\n setModel: connection.scan.setModel,\n start: connection.scan.startScan,\n stop: connection.scan.stopScan,\n }\n}\n\nexport function useMentraBluetooth(options: UseMentraBluetoothOptions = {}): MentraBluetoothSession {\n const connection = useGlassesConnection({\n autoConnectDefault: options.autoConnectDefault,\n defaultDeviceStorage: options.defaultDeviceStorage,\n onError: options.onError,\n scanModel: options.defaultModel ?? DeviceModels.MentraLive,\n scanTimeoutMs: options.scanTimeoutMs,\n })\n const [galleryModeApplying, setGalleryModeApplying] = useState(false)\n const [galleryModeError, setGalleryModeError] = useState<unknown | null>(null)\n\n async function setGalleryModeEnabled(enabled: boolean) {\n setGalleryModeApplying(true)\n setGalleryModeError(null)\n try {\n await BluetoothSdk.setGalleryModeEnabled(enabled)\n } catch (error) {\n setGalleryModeError(error)\n options.onError?.(error)\n throw error\n } finally {\n setGalleryModeApplying(false)\n }\n }\n\n async function setVoiceActivityDetectionEnabled(enabled: boolean) {\n try {\n await BluetoothSdk.setVoiceActivityDetectionEnabled(enabled)\n } catch (error) {\n options.onError?.(error)\n throw error\n }\n }\n\n const galleryMode: GalleryModeState = {\n applying: galleryModeApplying,\n enabled: connection.bluetoothStatus.galleryModeEnabled !== false,\n error: galleryModeError,\n }\n\n return {\n busy: connection.busy || galleryModeApplying,\n clearDefaultDevice: connection.clearDefaultDevice,\n connect: connection.connect,\n connectDefault: connection.connectDefault,\n defaultDevice: connection.defaultDevice,\n disconnect: connection.disconnect,\n error: galleryModeError ?? connection.error,\n glasses: runtimeGlassesState(connection.glassesStatus),\n refresh: connection.refresh,\n scan: scanController(connection),\n sdk: phoneSdkState(connection.bluetoothStatus, connection.defaultDevice, galleryMode),\n setDefaultDevice: connection.setDefaultDevice,\n setGalleryModeEnabled,\n setVoiceActivityDetectionEnabled,\n }\n}\n"]}
|
|
@@ -19,7 +19,8 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
19
19
|
"button_press",
|
|
20
20
|
"touch_event",
|
|
21
21
|
"head_up",
|
|
22
|
-
"
|
|
22
|
+
"voice_activity_detection_status",
|
|
23
|
+
"speaking_status",
|
|
23
24
|
"battery_status",
|
|
24
25
|
"wifi_status_change",
|
|
25
26
|
"hotspot_status_change",
|
|
@@ -291,21 +292,14 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
291
292
|
|
|
292
293
|
// MARK: - Gallery Commands
|
|
293
294
|
|
|
294
|
-
AsyncFunction("
|
|
295
|
-
let
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
galleryMode = .manual
|
|
301
|
-
default:
|
|
302
|
-
throw BluetoothError(
|
|
303
|
-
code: "invalid_gallery_mode",
|
|
304
|
-
message: "setGalleryMode mode must be \"auto\" or \"manual\"."
|
|
305
|
-
)
|
|
306
|
-
}
|
|
295
|
+
AsyncFunction("setGalleryModeEnabled") { (enabled: Bool) in
|
|
296
|
+
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
297
|
+
try await sdk.setGalleryModeEnabled(enabled)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
AsyncFunction("setVoiceActivityDetectionEnabled") { (enabled: Bool) in
|
|
307
301
|
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
308
|
-
try await sdk.
|
|
302
|
+
try await sdk.setVoiceActivityDetectionEnabled(enabled)
|
|
309
303
|
}
|
|
310
304
|
|
|
311
305
|
AsyncFunction("queryGalleryStatus") {
|
|
@@ -314,11 +308,30 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
314
308
|
}
|
|
315
309
|
}
|
|
316
310
|
|
|
317
|
-
AsyncFunction("requestPhoto") {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
311
|
+
AsyncFunction("requestPhoto") { (params: [String: Any]) in
|
|
312
|
+
let requestId = params["requestId"] as? String ?? ""
|
|
313
|
+
let appId = params["appId"] as? String ?? ""
|
|
314
|
+
Bridge.log(
|
|
315
|
+
"NATIVE: PHOTO PIPELINE [3/6] BluetoothSdk.requestPhoto requestId=\(requestId) appId=\(appId)"
|
|
316
|
+
)
|
|
317
|
+
let size = params["size"] as? String ?? "medium"
|
|
318
|
+
let webhookUrl = params["webhookUrl"] as? String ?? ""
|
|
319
|
+
let authToken = params["authToken"] as? String ?? ""
|
|
320
|
+
let compress = params["compress"] as? String ?? "none"
|
|
321
|
+
let flash = params["flash"] as? Bool ?? true
|
|
322
|
+
let sound = params["sound"] as? Bool ?? true
|
|
323
|
+
let exposureTimeNs: Double?
|
|
324
|
+
switch params["exposureTimeNs"] {
|
|
325
|
+
case let value as Double:
|
|
326
|
+
exposureTimeNs = value
|
|
327
|
+
case let value as Int:
|
|
328
|
+
exposureTimeNs = Double(value)
|
|
329
|
+
case let value as NSNumber:
|
|
330
|
+
exposureTimeNs = value.doubleValue
|
|
331
|
+
default:
|
|
332
|
+
exposureTimeNs = nil
|
|
333
|
+
}
|
|
334
|
+
|
|
322
335
|
await MainActor.run {
|
|
323
336
|
self.bluetoothSdk().requestPhoto(
|
|
324
337
|
PhotoRequest(
|
|
@@ -327,8 +340,10 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
327
340
|
size: PhotoSize(rawValue: size) ?? .medium,
|
|
328
341
|
webhookUrl: webhookUrl,
|
|
329
342
|
authToken: authToken,
|
|
330
|
-
compress:
|
|
331
|
-
|
|
343
|
+
compress: PhotoCompression(rawValue: compress),
|
|
344
|
+
flash: flash,
|
|
345
|
+
sound: sound,
|
|
346
|
+
exposureTimeNs: exposureTimeNs
|
|
332
347
|
)
|
|
333
348
|
)
|
|
334
349
|
}
|
|
@@ -449,7 +464,6 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
449
464
|
AsyncFunction("setMicState") { (
|
|
450
465
|
enabled: Bool,
|
|
451
466
|
useGlassesMic: Bool?,
|
|
452
|
-
bypassVad: Bool?,
|
|
453
467
|
sendTranscript: Bool?,
|
|
454
468
|
sendLc3Data: Bool?
|
|
455
469
|
) in
|
|
@@ -457,7 +471,6 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
457
471
|
self.bluetoothSdk().setMicState(
|
|
458
472
|
enabled: enabled,
|
|
459
473
|
useGlassesMic: useGlassesMic ?? true,
|
|
460
|
-
bypassVad: bypassVad ?? true,
|
|
461
474
|
sendTranscript: sendTranscript ?? false,
|
|
462
475
|
sendLc3Data: sendLc3Data ?? false
|
|
463
476
|
)
|
|
@@ -569,6 +582,10 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
569
582
|
)
|
|
570
583
|
case let .touch(touch):
|
|
571
584
|
sendEvent("touch_event", touch.values)
|
|
585
|
+
case let .voiceActivityDetectionStatus(status):
|
|
586
|
+
sendEvent("voice_activity_detection_status", status.values)
|
|
587
|
+
case let .speakingStatus(status):
|
|
588
|
+
sendEvent("speaking_status", status.values)
|
|
572
589
|
case let .wifiStatus(status):
|
|
573
590
|
sendEvent("wifi_status_change", status.values)
|
|
574
591
|
case let .hotspotStatus(status):
|
|
@@ -45,8 +45,6 @@ Pod::Spec.new do |s|
|
|
|
45
45
|
# Vendored frameworks
|
|
46
46
|
s.vendored_frameworks = 'Packages/SherpaOnnx/sherpa-onnx.xcframework'
|
|
47
47
|
|
|
48
|
-
# Resources (model files)
|
|
49
|
-
s.resources = 'Packages/VAD/Silero/Model/*.onnx'
|
|
50
48
|
s.resource_bundles = {
|
|
51
49
|
'BluetoothSDKPrivacy' => ['Source/PrivacyInfo.xcprivacy']
|
|
52
50
|
}
|
|
@@ -56,7 +54,6 @@ Pod::Spec.new do |s|
|
|
|
56
54
|
"Packages/CoreObjC/**/*.{h,m,mm,hpp,cpp,c}",
|
|
57
55
|
"Packages/SherpaOnnx/SherpaOnnx.swift",
|
|
58
56
|
"Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/**/*.{h,hpp}",
|
|
59
|
-
"Packages/VAD/**/*.swift",
|
|
60
57
|
"Packages/libbz2/shim.h"
|
|
61
58
|
]
|
|
62
59
|
native_source_files << "BluetoothSdkModule.swift" if include_expo_adapter
|
|
@@ -34,7 +34,7 @@ public struct MicPcmEvent: CustomStringConvertible {
|
|
|
34
34
|
public let bitsPerSample: Int
|
|
35
35
|
public let channels: Int
|
|
36
36
|
public let encoding: String
|
|
37
|
-
public let
|
|
37
|
+
public let voiceActivityDetectionEnabled: Bool
|
|
38
38
|
public let values: [String: Any]
|
|
39
39
|
|
|
40
40
|
public init(values: [String: Any]) {
|
|
@@ -43,7 +43,7 @@ public struct MicPcmEvent: CustomStringConvertible {
|
|
|
43
43
|
let bitsPerSample = intValue(values["bitsPerSample"]) ?? Self.bitsPerSample
|
|
44
44
|
let channels = intValue(values["channels"]) ?? Self.channels
|
|
45
45
|
let encoding = values["encoding"] as? String ?? Self.encoding
|
|
46
|
-
let
|
|
46
|
+
let voiceActivityDetectionEnabled = boolValue(values, "voiceActivityDetectionEnabled") ?? true
|
|
47
47
|
|
|
48
48
|
var normalized = values
|
|
49
49
|
normalized["type"] = "mic_pcm"
|
|
@@ -52,19 +52,19 @@ public struct MicPcmEvent: CustomStringConvertible {
|
|
|
52
52
|
normalized["bitsPerSample"] = bitsPerSample
|
|
53
53
|
normalized["channels"] = channels
|
|
54
54
|
normalized["encoding"] = encoding
|
|
55
|
-
normalized["
|
|
55
|
+
normalized["voiceActivityDetectionEnabled"] = voiceActivityDetectionEnabled
|
|
56
56
|
|
|
57
57
|
self.pcm = pcm
|
|
58
58
|
self.sampleRate = sampleRate
|
|
59
59
|
self.bitsPerSample = bitsPerSample
|
|
60
60
|
self.channels = channels
|
|
61
61
|
self.encoding = encoding
|
|
62
|
-
self.
|
|
62
|
+
self.voiceActivityDetectionEnabled = voiceActivityDetectionEnabled
|
|
63
63
|
self.values = normalized
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
public var description: String {
|
|
67
|
-
"MicPcmEvent(bytes: \(pcm.count), sampleRate: \(sampleRate), bitsPerSample: \(bitsPerSample), channels: \(channels), encoding: \(encoding),
|
|
67
|
+
"MicPcmEvent(bytes: \(pcm.count), sampleRate: \(sampleRate), bitsPerSample: \(bitsPerSample), channels: \(channels), encoding: \(encoding), voiceActivityDetectionEnabled: \(voiceActivityDetectionEnabled))"
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -83,7 +83,7 @@ public struct MicLc3Event: CustomStringConvertible {
|
|
|
83
83
|
public let frameSizeBytes: Int
|
|
84
84
|
public let bitrate: Int
|
|
85
85
|
public let packetizedFromGlasses: Bool
|
|
86
|
-
public let
|
|
86
|
+
public let voiceActivityDetectionEnabled: Bool
|
|
87
87
|
public let values: [String: Any]
|
|
88
88
|
|
|
89
89
|
public init(values: [String: Any]) {
|
|
@@ -95,7 +95,7 @@ public struct MicLc3Event: CustomStringConvertible {
|
|
|
95
95
|
let frameSizeBytes = intValue(values["frameSizeBytes"]) ?? Self.defaultFrameSizeBytes
|
|
96
96
|
let bitrate = intValue(values["bitrate"]) ?? frameSizeBytes * 8 * (1000 / frameDurationMs)
|
|
97
97
|
let packetizedFromGlasses = boolValue(values, "packetizedFromGlasses") ?? false
|
|
98
|
-
let
|
|
98
|
+
let voiceActivityDetectionEnabled = boolValue(values, "voiceActivityDetectionEnabled") ?? true
|
|
99
99
|
|
|
100
100
|
var normalized = values
|
|
101
101
|
normalized["type"] = "mic_lc3"
|
|
@@ -107,7 +107,7 @@ public struct MicLc3Event: CustomStringConvertible {
|
|
|
107
107
|
normalized["frameSizeBytes"] = frameSizeBytes
|
|
108
108
|
normalized["bitrate"] = bitrate
|
|
109
109
|
normalized["packetizedFromGlasses"] = packetizedFromGlasses
|
|
110
|
-
normalized["
|
|
110
|
+
normalized["voiceActivityDetectionEnabled"] = voiceActivityDetectionEnabled
|
|
111
111
|
|
|
112
112
|
self.lc3 = lc3
|
|
113
113
|
self.sampleRate = sampleRate
|
|
@@ -117,12 +117,12 @@ public struct MicLc3Event: CustomStringConvertible {
|
|
|
117
117
|
self.frameSizeBytes = frameSizeBytes
|
|
118
118
|
self.bitrate = bitrate
|
|
119
119
|
self.packetizedFromGlasses = packetizedFromGlasses
|
|
120
|
-
self.
|
|
120
|
+
self.voiceActivityDetectionEnabled = voiceActivityDetectionEnabled
|
|
121
121
|
self.values = normalized
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
public var description: String {
|
|
125
|
-
"MicLc3Event(bytes: \(lc3.count), sampleRate: \(sampleRate), channels: \(channels), frameDurationMs: \(frameDurationMs), frameSizeBytes: \(frameSizeBytes), bitrate: \(bitrate), packetizedFromGlasses: \(packetizedFromGlasses),
|
|
125
|
+
"MicLc3Event(bytes: \(lc3.count), sampleRate: \(sampleRate), channels: \(channels), frameDurationMs: \(frameDurationMs), frameSizeBytes: \(frameSizeBytes), bitrate: \(bitrate), packetizedFromGlasses: \(packetizedFromGlasses), voiceActivityDetectionEnabled: \(voiceActivityDetectionEnabled))"
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
package/ios/Source/Bridge.swift
CHANGED
|
@@ -89,18 +89,22 @@ class Bridge {
|
|
|
89
89
|
|
|
90
90
|
@MainActor
|
|
91
91
|
private static func micPcmEventBody(_ data: Data) -> [String: Any] {
|
|
92
|
-
|
|
92
|
+
let voiceActivityDetectionEnabled =
|
|
93
|
+
DeviceStore.shared.get("glasses", "voiceActivityDetectionEnabled") as? Bool ?? true
|
|
94
|
+
return [
|
|
93
95
|
"pcm": data,
|
|
94
96
|
"sampleRate": micSampleRate,
|
|
95
97
|
"bitsPerSample": pcmBitsPerSample,
|
|
96
98
|
"channels": micChannels,
|
|
97
99
|
"encoding": "pcm_s16le",
|
|
98
|
-
"
|
|
100
|
+
"voiceActivityDetectionEnabled": voiceActivityDetectionEnabled,
|
|
99
101
|
]
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
@MainActor
|
|
103
105
|
private static func micLc3EventBody(_ data: Data) -> [String: Any] {
|
|
106
|
+
let voiceActivityDetectionEnabled =
|
|
107
|
+
DeviceStore.shared.get("glasses", "voiceActivityDetectionEnabled") as? Bool ?? true
|
|
104
108
|
let frameSizeBytes = DeviceStore.shared.get("bluetooth", "lc3_frame_size") as? Int ?? defaultLc3FrameSizeBytes
|
|
105
109
|
return [
|
|
106
110
|
"lc3": data,
|
|
@@ -111,23 +115,30 @@ class Bridge {
|
|
|
111
115
|
"frameSizeBytes": frameSizeBytes,
|
|
112
116
|
"bitrate": frameSizeBytes * 8 * (1000 / lc3FrameDurationMs),
|
|
113
117
|
"packetizedFromGlasses": false,
|
|
114
|
-
"
|
|
118
|
+
"voiceActivityDetectionEnabled": voiceActivityDetectionEnabled,
|
|
115
119
|
]
|
|
116
120
|
}
|
|
117
121
|
|
|
118
|
-
@MainActor
|
|
119
|
-
private static func isVadGated() -> Bool {
|
|
120
|
-
!(DeviceStore.shared.get("bluetooth", "bypass_vad") as? Bool ?? true)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
122
|
static func saveSetting(_ key: String, _ value: Any) {
|
|
124
123
|
let body = ["key": key, "value": value]
|
|
125
124
|
Bridge.sendTypedMessage("save_setting", body: body)
|
|
126
125
|
}
|
|
127
126
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
@MainActor
|
|
128
|
+
static func sendVoiceActivityDetectionStatus(_ enabled: Bool) {
|
|
129
|
+
DeviceStore.shared.set("glasses", "voiceActivityDetectionEnabled", enabled)
|
|
130
|
+
let body: [String: Any] = [
|
|
131
|
+
"voiceActivityDetectionEnabled": enabled,
|
|
132
|
+
]
|
|
133
|
+
Bridge.sendTypedMessage("voice_activity_detection_status", body: body)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static func sendSpeakingStatus(_ speaking: Bool) {
|
|
137
|
+
let body: [String: Any] = [
|
|
138
|
+
"speaking": speaking,
|
|
139
|
+
"timestamp": Int(Date().timeIntervalSince1970 * 1000),
|
|
140
|
+
]
|
|
141
|
+
Bridge.sendTypedMessage("speaking_status", body: body)
|
|
131
142
|
}
|
|
132
143
|
|
|
133
144
|
static func sendBatteryStatus(level: Int, charging: Bool) {
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
|
|
3
|
-
public enum GalleryMode {
|
|
4
|
-
case auto
|
|
5
|
-
case manual
|
|
6
|
-
}
|
|
7
|
-
|
|
8
3
|
public enum PhotoSize: String {
|
|
9
4
|
case small
|
|
10
5
|
case medium
|
|
@@ -35,12 +30,12 @@ public struct ButtonPhotoSettings {
|
|
|
35
30
|
public struct ButtonVideoRecordingSettings {
|
|
36
31
|
public let width: Int
|
|
37
32
|
public let height: Int
|
|
38
|
-
public let
|
|
33
|
+
public let fps: Int
|
|
39
34
|
|
|
40
|
-
public init(width: Int, height: Int,
|
|
35
|
+
public init(width: Int, height: Int, fps: Int) {
|
|
41
36
|
self.width = width
|
|
42
37
|
self.height = height
|
|
43
|
-
self.
|
|
38
|
+
self.fps = fps
|
|
44
39
|
}
|
|
45
40
|
}
|
|
46
41
|
|
|
@@ -51,9 +46,9 @@ public enum CameraFov {
|
|
|
51
46
|
var value: [String: Int] {
|
|
52
47
|
switch self {
|
|
53
48
|
case .standard:
|
|
54
|
-
["fov": 118, "
|
|
49
|
+
["fov": 118, "roi_position": 0]
|
|
55
50
|
case .wide:
|
|
56
|
-
["fov": 118, "
|
|
51
|
+
["fov": 118, "roi_position": 0]
|
|
57
52
|
}
|
|
58
53
|
}
|
|
59
54
|
}
|
|
@@ -65,7 +60,10 @@ public struct PhotoRequest {
|
|
|
65
60
|
public let webhookUrl: String?
|
|
66
61
|
public let authToken: String?
|
|
67
62
|
public let compress: PhotoCompression?
|
|
63
|
+
public let flash: Bool
|
|
68
64
|
public let sound: Bool
|
|
65
|
+
/// Sensor exposure time for this capture only (ns), or nil for auto exposure
|
|
66
|
+
public let exposureTimeNs: Double?
|
|
69
67
|
|
|
70
68
|
public init(
|
|
71
69
|
requestId: String,
|
|
@@ -74,7 +72,9 @@ public struct PhotoRequest {
|
|
|
74
72
|
webhookUrl: String? = nil,
|
|
75
73
|
authToken: String? = nil,
|
|
76
74
|
compress: PhotoCompression? = nil,
|
|
77
|
-
|
|
75
|
+
flash: Bool = true,
|
|
76
|
+
sound: Bool,
|
|
77
|
+
exposureTimeNs: Double? = nil
|
|
78
78
|
) {
|
|
79
79
|
self.requestId = requestId
|
|
80
80
|
self.appId = appId
|
|
@@ -82,7 +82,9 @@ public struct PhotoRequest {
|
|
|
82
82
|
self.webhookUrl = webhookUrl
|
|
83
83
|
self.authToken = authToken
|
|
84
84
|
self.compress = compress
|
|
85
|
+
self.flash = flash
|
|
85
86
|
self.sound = sound
|
|
87
|
+
self.exposureTimeNs = exposureTimeNs
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
|
|
@@ -167,11 +167,6 @@ struct ViewState {
|
|
|
167
167
|
set { DeviceStore.shared.apply("bluetooth", "sensing_enabled", newValue) }
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
private var bypassVad: Bool {
|
|
171
|
-
get { DeviceStore.shared.get("bluetooth", "bypass_vad") as? Bool ?? true }
|
|
172
|
-
set { DeviceStore.shared.apply("bluetooth", "bypass_vad", newValue) }
|
|
173
|
-
}
|
|
174
|
-
|
|
175
170
|
private var offlineCaptionsRunning: Bool {
|
|
176
171
|
get { DeviceStore.shared.get("bluetooth", "offline_captions_running") as? Bool ?? false }
|
|
177
172
|
set { DeviceStore.shared.apply("bluetooth", "offline_captions_running", newValue) }
|
|
@@ -285,11 +280,6 @@ struct ViewState {
|
|
|
285
280
|
private var lastLc3Event: Date?
|
|
286
281
|
private var micReinitTimer: Timer?
|
|
287
282
|
|
|
288
|
-
// VAD:
|
|
289
|
-
private var vad: SileroVADStrategy?
|
|
290
|
-
private var vadBuffer = [Data]()
|
|
291
|
-
private var isSpeaking = false
|
|
292
|
-
|
|
293
283
|
/// STT:
|
|
294
284
|
private var transcriber: SherpaOnnxTranscriber?
|
|
295
285
|
|
|
@@ -314,7 +304,6 @@ struct ViewState {
|
|
|
314
304
|
|
|
315
305
|
override init() {
|
|
316
306
|
Bridge.log("MAN: init()")
|
|
317
|
-
vad = SileroVADStrategy()
|
|
318
307
|
super.init()
|
|
319
308
|
|
|
320
309
|
// Start memory monitoring (logs every 30s to help detect leaks)
|
|
@@ -336,16 +325,6 @@ struct ViewState {
|
|
|
336
325
|
Bridge.log("SherpaOnnxTranscriber fully initialized")
|
|
337
326
|
}
|
|
338
327
|
|
|
339
|
-
Task {
|
|
340
|
-
self.vad?.setup(
|
|
341
|
-
sampleRate: .rate_16k,
|
|
342
|
-
frameSize: .size_1024,
|
|
343
|
-
quality: .normal,
|
|
344
|
-
silenceTriggerDurationMs: 4000,
|
|
345
|
-
speechTriggerDurationMs: 50
|
|
346
|
-
)
|
|
347
|
-
}
|
|
348
|
-
|
|
349
328
|
// Initialize persistent LC3 converter for unified audio encoding
|
|
350
329
|
lc3Converter = PcmConverter()
|
|
351
330
|
Bridge.log("LC3 converter initialized for unified audio encoding")
|
|
@@ -362,13 +341,6 @@ struct ViewState {
|
|
|
362
341
|
|
|
363
342
|
// MARK: - AUX Voice Data Handling
|
|
364
343
|
|
|
365
|
-
private func checkSetVadStatus(speaking: Bool) {
|
|
366
|
-
if speaking != isSpeaking {
|
|
367
|
-
isSpeaking = speaking
|
|
368
|
-
Bridge.sendVadEvent(isSpeaking)
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
344
|
private func convertAndSendMicLc3(_ pcmData: Data) {
|
|
373
345
|
guard let lc3Converter = lc3Converter else {
|
|
374
346
|
Bridge.log("MAN: ERROR - LC3 converter not initialized but format is LC3")
|
|
@@ -393,23 +365,6 @@ struct ViewState {
|
|
|
393
365
|
}
|
|
394
366
|
}
|
|
395
367
|
|
|
396
|
-
private func emptyVadBuffer() {
|
|
397
|
-
// go through the buffer, popping from the first element in the array (FIFO):
|
|
398
|
-
while !vadBuffer.isEmpty {
|
|
399
|
-
let chunk = vadBuffer.removeFirst()
|
|
400
|
-
handleSendingPcm(chunk)
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
private func addToVadBuffer(_ chunk: Data) {
|
|
405
|
-
let MAX_BUFFER_SIZE = 20
|
|
406
|
-
vadBuffer.append(chunk)
|
|
407
|
-
while vadBuffer.count > MAX_BUFFER_SIZE {
|
|
408
|
-
// pop from the front of the array:
|
|
409
|
-
vadBuffer.removeFirst()
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
368
|
/**
|
|
414
369
|
* Handle raw LC3 audio data from glasses.
|
|
415
370
|
* Decodes the glasses LC3 to PCM, then forwards to handlePcm for processing.
|
|
@@ -432,59 +387,16 @@ struct ViewState {
|
|
|
432
387
|
Bridge.log("MAN: Failed to decode glasses LC3 audio")
|
|
433
388
|
return
|
|
434
389
|
}
|
|
435
|
-
// Forward to handlePcm which handles
|
|
390
|
+
// Forward to handlePcm which handles SDK audio events and encoding.
|
|
436
391
|
handlePcm(pcmData)
|
|
437
392
|
}
|
|
438
393
|
|
|
439
394
|
func handlePcm(_ pcmData: Data) {
|
|
440
|
-
|
|
441
|
-
if bypassVad {
|
|
442
|
-
handleSendingPcm(pcmData)
|
|
395
|
+
handleSendingPcm(pcmData)
|
|
443
396
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
}
|
|
448
|
-
return
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// feed PCM to the VAD:
|
|
452
|
-
guard let vad = vad else {
|
|
453
|
-
Bridge.log("VAD not initialized")
|
|
454
|
-
return
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// convert audioData to Int16 array for VAD:
|
|
458
|
-
let pcmDataArray = pcmData.withUnsafeBytes { pointer -> [Int16] in
|
|
459
|
-
Array(
|
|
460
|
-
UnsafeBufferPointer(
|
|
461
|
-
start: pointer.bindMemory(to: Int16.self).baseAddress,
|
|
462
|
-
count: pointer.count / MemoryLayout<Int16>.stride
|
|
463
|
-
)
|
|
464
|
-
)
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
vad.checkVAD(pcm: pcmDataArray) { [weak self] state in
|
|
468
|
-
guard let self = self else { return }
|
|
469
|
-
Bridge.log("VAD State: \(state)")
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
let vadState = vad.currentState()
|
|
473
|
-
if vadState == .speeching {
|
|
474
|
-
checkSetVadStatus(speaking: true)
|
|
475
|
-
// first send out whatever's in the vadBuffer (if there is anything):
|
|
476
|
-
emptyVadBuffer()
|
|
477
|
-
|
|
478
|
-
handleSendingPcm(pcmData)
|
|
479
|
-
|
|
480
|
-
// Send PCM to local transcriber (always needs raw PCM)
|
|
481
|
-
if shouldSendTranscript || offlineCaptionsRunning || localSttFallbackActive {
|
|
482
|
-
transcriber?.acceptAudio(pcm16le: pcmData)
|
|
483
|
-
}
|
|
484
|
-
} else {
|
|
485
|
-
checkSetVadStatus(speaking: false)
|
|
486
|
-
// add to the vadBuffer (stores PCM for potential later sending):
|
|
487
|
-
addToVadBuffer(pcmData)
|
|
397
|
+
// Send PCM to local transcriber.
|
|
398
|
+
if shouldSendTranscript || offlineCaptionsRunning || localSttFallbackActive {
|
|
399
|
+
transcriber?.acceptAudio(pcm16le: pcmData)
|
|
488
400
|
}
|
|
489
401
|
}
|
|
490
402
|
|
|
@@ -1013,6 +925,8 @@ struct ViewState {
|
|
|
1013
925
|
|
|
1014
926
|
func handleDeviceDisconnected() {
|
|
1015
927
|
Bridge.log("MAN: Device disconnected")
|
|
928
|
+
DeviceStore.shared.apply("glasses", "headUp", false)
|
|
929
|
+
DeviceStore.shared.apply("glasses", "voiceActivityDetectionEnabled", true)
|
|
1016
930
|
// shouldSendBootingMessage = true // Reset for next first connect
|
|
1017
931
|
}
|
|
1018
932
|
|
|
@@ -1280,14 +1194,22 @@ struct ViewState {
|
|
|
1280
1194
|
_ webhookUrl: String?,
|
|
1281
1195
|
_ authToken: String?,
|
|
1282
1196
|
_ compress: String?,
|
|
1283
|
-
_
|
|
1197
|
+
_ flash: Bool,
|
|
1198
|
+
_ sound: Bool,
|
|
1199
|
+
exposureTimeNs: Double? = nil
|
|
1284
1200
|
) {
|
|
1285
1201
|
Bridge.log(
|
|
1286
|
-
"MAN: requestPhoto
|
|
1202
|
+
"MAN: PHOTO PIPELINE [4/6] DeviceManager.requestPhoto requestId=\(requestId) appId=\(appId) webhookUrl=\(webhookUrl ?? "nil") size=\(size) compress=\(compress ?? "none") flash=\(flash) sound=\(sound) exposureTimeNs=\(exposureTimeNs.map { String($0) } ?? "nil") sgc=\(sgc != nil ? String(describing: type(of: sgc!)) : "null")"
|
|
1287
1203
|
)
|
|
1288
|
-
sgc
|
|
1204
|
+
guard let sgc else {
|
|
1205
|
+
Bridge.log(
|
|
1206
|
+
"MAN: PHOTO PIPELINE — sgc is null (glasses not connected); dropping requestId=\(requestId)"
|
|
1207
|
+
)
|
|
1208
|
+
return
|
|
1209
|
+
}
|
|
1210
|
+
sgc.requestPhoto(
|
|
1289
1211
|
requestId, appId: appId, size: size, webhookUrl: webhookUrl, authToken: authToken,
|
|
1290
|
-
compress: compress, flash:
|
|
1212
|
+
compress: compress, flash: flash, sound: sound, exposureTimeNs: exposureTimeNs
|
|
1291
1213
|
)
|
|
1292
1214
|
}
|
|
1293
1215
|
|
|
@@ -1393,6 +1315,7 @@ struct ViewState {
|
|
|
1393
1315
|
DeviceStore.shared.apply("glasses", "deviceModel", "")
|
|
1394
1316
|
DeviceStore.shared.apply("glasses", "fullyBooted", false)
|
|
1395
1317
|
DeviceStore.shared.apply("glasses", "connected", false)
|
|
1318
|
+
DeviceStore.shared.apply("glasses", "voiceActivityDetectionEnabled", true)
|
|
1396
1319
|
// disconnect the controller as well:
|
|
1397
1320
|
searchingController = false
|
|
1398
1321
|
DeviceStore.shared.apply("glasses", "controllerConnected", false)
|