| import { createRequire } from 'node:module' |
| import { dirname, resolve, sep } from 'node:path' |
| import { fileURLToPath } from 'node:url' |
| |
| |
| |
| const nodeRequire = createRequire(import.meta.url) |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| function getVendorRoot(): string { |
| const filePath = fileURLToPath(import.meta.url) |
| const dir = dirname(filePath) |
| const parts = dir.split(sep) |
| const distIdx = parts.lastIndexOf('dist') |
| if (distIdx !== -1) { |
| return parts.slice(0, distIdx + 1).join(sep) + sep + 'vendor' |
| } |
| |
| return resolve(dir, '..', '..', '..', 'vendor') |
| } |
|
|
| type AudioCaptureNapi = { |
| startRecording(onData: (data: Buffer) => void, onEnd: () => void): boolean |
| stopRecording(): void |
| isRecording(): boolean |
| startPlayback(sampleRate: number, channels: number): boolean |
| writePlaybackData(data: Buffer): void |
| stopPlayback(): void |
| isPlaying(): boolean |
| |
| |
| |
| |
| |
| microphoneAuthorizationStatus?(): number |
| } |
|
|
| let cachedModule: AudioCaptureNapi | null = null |
| let loadAttempted = false |
|
|
| function loadModule(): AudioCaptureNapi | null { |
| if (loadAttempted) { |
| return cachedModule |
| } |
| loadAttempted = true |
|
|
| |
| const platform = process.platform |
| if (platform !== 'darwin' && platform !== 'linux' && platform !== 'win32') { |
| return null |
| } |
|
|
| |
| |
| |
| |
| |
| if (process.env.AUDIO_CAPTURE_NODE_PATH) { |
| try { |
| |
| cachedModule = nodeRequire( |
| process.env.AUDIO_CAPTURE_NODE_PATH, |
| ) as AudioCaptureNapi |
| return cachedModule |
| } catch { |
| |
| } |
| } |
|
|
| |
| |
| |
| const platformDir = `${process.arch}-${platform}` |
| const binaryRel = `audio-capture/${platformDir}/audio-capture.node` |
| const vendorRoot = getVendorRoot() |
| const fallbacks = [ |
| resolve(vendorRoot, binaryRel), |
| `./vendor/${binaryRel}`, |
| `../vendor/${binaryRel}`, |
| `../../vendor/${binaryRel}`, |
| `${process.cwd()}/vendor/${binaryRel}`, |
| ] |
| for (const p of fallbacks) { |
| try { |
| |
| cachedModule = nodeRequire(p) as AudioCaptureNapi |
| return cachedModule |
| } catch { |
| |
| } |
| } |
| return null |
| } |
|
|
| export function isNativeAudioAvailable(): boolean { |
| return loadModule() !== null |
| } |
|
|
| export function startNativeRecording( |
| onData: (data: Buffer) => void, |
| onEnd: () => void, |
| ): boolean { |
| const mod = loadModule() |
| if (!mod) { |
| return false |
| } |
| return mod.startRecording(onData, onEnd) |
| } |
|
|
| export function stopNativeRecording(): void { |
| const mod = loadModule() |
| if (!mod) { |
| return |
| } |
| mod.stopRecording() |
| } |
|
|
| export function isNativeRecordingActive(): boolean { |
| const mod = loadModule() |
| if (!mod) { |
| return false |
| } |
| return mod.isRecording() |
| } |
|
|
| export function startNativePlayback( |
| sampleRate: number, |
| channels: number, |
| ): boolean { |
| const mod = loadModule() |
| if (!mod) { |
| return false |
| } |
| return mod.startPlayback(sampleRate, channels) |
| } |
|
|
| export function writeNativePlaybackData(data: Buffer): void { |
| const mod = loadModule() |
| if (!mod) { |
| return |
| } |
| mod.writePlaybackData(data) |
| } |
|
|
| export function stopNativePlayback(): void { |
| const mod = loadModule() |
| if (!mod) { |
| return |
| } |
| mod.stopPlayback() |
| } |
|
|
| export function isNativePlaying(): boolean { |
| const mod = loadModule() |
| if (!mod) { |
| return false |
| } |
| return mod.isPlaying() |
| } |
|
|
| |
| |
| |
| |
| |
| export function microphoneAuthorizationStatus(): number { |
| const mod = loadModule() |
| if (!mod || !mod.microphoneAuthorizationStatus) { |
| return 0 |
| } |
| return mod.microphoneAuthorizationStatus() |
| } |
|
|