feat(native): windows audio monitoring & recording (#12615)

fix AF-2692

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added comprehensive Windows support for audio and application capture,
including real-time microphone usage detection, combined microphone and
system audio recording, and application state monitoring.
  - The "meetings" setting is now enabled on Windows as well as macOS.
- Conditional UI styling and attributes introduced for Windows
environments in the Electron renderer.

- **Bug Fixes**
- Enhanced file path handling and validation for Windows in Electron
file requests.

- **Refactor**
- Unified application info handling across platforms by consolidating
types into a single `ApplicationInfo` structure.
- Updated native module APIs by removing deprecated types, refining
method signatures, and improving error messages.
- Streamlined audio tapping APIs to use process IDs and consistent
callback types.

- **Documentation**
- Added detailed documentation for the Windows-specific audio recording
and microphone listener modules.

- **Chores**
  - Updated development dependencies in multiple packages.
- Reorganized and added platform-specific dependencies and configuration
for Windows support.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->





#### PR Dependency Tree


* **PR #12615** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

---------

Co-authored-by: LongYinan <lynweklm@gmail.com>
This commit is contained in:
Peng Xiao
2025-06-18 13:57:01 +08:00
committed by GitHub
co-authored by LongYinan
parent c844786a7f
commit 899ffd1ad3
27 changed files with 2509 additions and 458 deletions
+57 -76
View File
@@ -1,11 +1,12 @@
/* auto-generated by NAPI-RS */
/* eslint-disable */
export declare class Application {
constructor(processId: number)
get processId(): number
export declare class ApplicationInfo {
processId: number
name: string
objectId: number
constructor(processId: number, name: string, objectId: number)
get processGroupId(): number
get bundleIdentifier(): string
get name(): string
get icon(): Buffer
}
@@ -18,12 +19,30 @@ export declare class ApplicationStateChangedSubscriber {
}
export declare class AudioCaptureSession {
stop(): void
get sampleRate(): number
get channels(): number
get actualSampleRate(): number
stop(): void
}
export declare class ShareableContent {
static onApplicationListChanged(callback: ((err: Error | null, ) => void)): ApplicationListChangedSubscriber
static onAppStateChanged(app: ApplicationInfo, callback: ((err: Error | null, ) => void)): ApplicationStateChangedSubscriber
constructor()
static applications(): Array<ApplicationInfo>
static applicationWithProcessId(processId: number): ApplicationInfo | null
static tapAudio(processId: number, audioStreamCallback: ((err: Error | null, arg: Float32Array) => void)): AudioCaptureSession
static tapGlobalAudio(excludedProcesses: Array<ApplicationInfo> | undefined | null, audioStreamCallback: ((err: Error | null, arg: Float32Array) => void)): AudioCaptureSession
static isUsingMicrophone(processId: number): boolean
}
export declare function decodeAudio(buf: Uint8Array, destSampleRate?: number | undefined | null, filename?: string | undefined | null, signal?: AbortSignal | undefined | null): Promise<Float32Array>
/** Decode audio file into a Float32Array */
export declare function decodeAudioSync(buf: Uint8Array, destSampleRate?: number | undefined | null, filename?: string | undefined | null): Float32Array
export declare function mintChallengeResponse(resource: string, bits?: number | undefined | null): Promise<string>
export declare function verifyChallengeResponse(response: string, bits: number, resource: string): Promise<boolean>
export declare class DocStorage {
constructor(path: string)
validate(): Promise<boolean>
@@ -64,21 +83,43 @@ export declare class DocStoragePool {
getBlobUploadedAt(universalId: string, peer: string, blobId: string): Promise<Date | null>
}
export declare class RecordingPermissions {
audio: boolean
screen: boolean
export interface Blob {
key: string
data: Uint8Array
mime: string
size: number
createdAt: Date
}
export declare class ShareableContent {
static onApplicationListChanged(callback: ((err: Error | null, ) => void)): ApplicationListChangedSubscriber
static onAppStateChanged(app: TappableApplication, callback: ((err: Error | null, ) => void)): ApplicationStateChangedSubscriber
constructor()
applications(): Array<TappableApplication>
applicationWithProcessId(processId: number): Application | null
tappableApplicationWithProcessId(processId: number): TappableApplication | null
static tapGlobalAudio(excludedProcesses: Array<TappableApplication> | undefined | null, audioStreamCallback: ((err: Error | null, arg: Float32Array) => void)): AudioCaptureSession
export interface DocClock {
docId: string
timestamp: Date
}
export interface DocRecord {
docId: string
bin: Uint8Array
timestamp: Date
}
export interface DocUpdate {
docId: string
timestamp: Date
bin: Uint8Array
}
export interface ListedBlob {
key: string
size: number
mime: string
createdAt: Date
}
export interface SetBlob {
key: string
data: Uint8Array
mime: string
}
export declare class SqliteConnection {
constructor(path: string)
connect(): Promise<void>
@@ -117,80 +158,22 @@ export declare class SqliteConnection {
checkpoint(): Promise<void>
}
export declare class TappableApplication {
constructor(objectId: AudioObjectID)
static fromApplication(app: Application, objectId: AudioObjectID): TappableApplication
get processId(): number
get processGroupId(): number
get bundleIdentifier(): string
get name(): string
get objectId(): number
get icon(): Buffer
get isRunning(): boolean
tapAudio(audioStreamCallback: ((err: Error | null, arg: Float32Array) => void)): AudioCaptureSession
}
export interface Blob {
key: string
data: Uint8Array
mime: string
size: number
createdAt: Date
}
export interface BlobRow {
key: string
data: Buffer
timestamp: Date
}
export declare function decodeAudio(buf: Uint8Array, destSampleRate?: number | undefined | null, filename?: string | undefined | null, signal?: AbortSignal | undefined | null): Promise<Float32Array>
/** Decode audio file into a Float32Array */
export declare function decodeAudioSync(buf: Uint8Array, destSampleRate?: number | undefined | null, filename?: string | undefined | null): Float32Array
export interface DocClock {
docId: string
timestamp: Date
}
export interface DocRecord {
docId: string
bin: Uint8Array
timestamp: Date
}
export interface DocTimestampRow {
docId?: string
timestamp: Date
}
export interface DocUpdate {
docId: string
timestamp: Date
bin: Uint8Array
}
export interface InsertRow {
docId?: string
data: Uint8Array
}
export interface ListedBlob {
key: string
size: number
mime: string
createdAt: Date
}
export declare function mintChallengeResponse(resource: string, bits?: number | undefined | null): Promise<string>
export interface SetBlob {
key: string
data: Uint8Array
mime: string
}
export interface UpdateRow {
id: number
timestamp: Date
@@ -205,5 +188,3 @@ export declare enum ValidationResult {
GeneralError = 3,
Valid = 4
}
export declare function verifyChallengeResponse(response: string, bits: number, resource: string): Promise<boolean>