@xipkg/calls-utils 0.1.43 → 0.1.45
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/dist/index.d.ts +12 -1
- package/dist/index.mjs +5 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,17 @@ declare function isChromiumBased(): boolean;
|
|
|
18
18
|
declare function isSafari(): boolean;
|
|
19
19
|
declare function isLocal(p: Participant): p is LocalParticipant;
|
|
20
20
|
declare function isMacintosh(): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Браузер сам добавляет в `enumerateDevices()` псевдо-записи `"default"` (и
|
|
23
|
+
* `"communications"` на Windows), которые указывают на РЕАЛЬНОЕ устройство,
|
|
24
|
+
* уже присутствующее в том же списке под своим настоящим id — то есть одно и
|
|
25
|
+
* то же физическое устройство пользователь видит дважды. Убираем эти
|
|
26
|
+
* дубликаты: выбор конкретного устройства из списка достаточен, а сама
|
|
27
|
+
* "default"-запись ещё и мешает надёжно определять активное устройство (см.
|
|
28
|
+
* `useResolvedActiveDeviceId` — `activeDeviceId`/`track.getDeviceId()` иногда
|
|
29
|
+
* тоже "застревают" на этом сентинеле, не называя конкретное устройство).
|
|
30
|
+
*/
|
|
31
|
+
declare function excludeOsDefaultDevices(devices: MediaDeviceInfo[] | undefined): MediaDeviceInfo[];
|
|
21
32
|
|
|
22
33
|
/**
|
|
23
34
|
* Утилита для воспроизведения звуков уведомлений в ВКС
|
|
@@ -69,4 +80,4 @@ declare const NOISE_CANCELLATION_EVENTS: {
|
|
|
69
80
|
type NoiseCancellationEvent = (typeof NOISE_CANCELLATION_EVENTS)[keyof typeof NOISE_CANCELLATION_EVENTS];
|
|
70
81
|
declare function trackNoiseCancellationEvent(event: NoiseCancellationEvent, data?: Record<string, string>): void;
|
|
71
82
|
|
|
72
|
-
export { NOISE_CANCELLATION_EVENTS, isChromiumBased, isFireFox, isLocal, isMacintosh, isSafari, playSound, playSoundOnDevice, playSpeakerTestTone, trackNoiseCancellationEvent, useFullScreen, useKeyPress, useMedia, usePhoneLayout };
|
|
83
|
+
export { NOISE_CANCELLATION_EVENTS, excludeOsDefaultDevices, isChromiumBased, isFireFox, isLocal, isMacintosh, isSafari, playSound, playSoundOnDevice, playSpeakerTestTone, trackNoiseCancellationEvent, useFullScreen, useKeyPress, useMedia, usePhoneLayout };
|
package/dist/index.mjs
CHANGED
|
@@ -93,6 +93,10 @@ function isLocal(p) {
|
|
|
93
93
|
function isMacintosh() {
|
|
94
94
|
return navigator.platform.indexOf("Mac") > -1;
|
|
95
95
|
}
|
|
96
|
+
var OS_DEFAULT_DEVICE_IDS = /* @__PURE__ */ new Set(["default", "communications"]);
|
|
97
|
+
function excludeOsDefaultDevices(devices) {
|
|
98
|
+
return devices?.filter((device) => !OS_DEFAULT_DEVICE_IDS.has(device.deviceId)) ?? [];
|
|
99
|
+
}
|
|
96
100
|
|
|
97
101
|
// src/sounds.ts
|
|
98
102
|
var SOUND_PATHS = {
|
|
@@ -227,6 +231,6 @@ function trackNoiseCancellationEvent(event, data) {
|
|
|
227
231
|
}
|
|
228
232
|
}
|
|
229
233
|
|
|
230
|
-
export { NOISE_CANCELLATION_EVENTS, isChromiumBased, isFireFox, isLocal, isMacintosh, isSafari, playSound, playSoundOnDevice, playSpeakerTestTone, trackNoiseCancellationEvent, useFullScreen, useKeyPress, useMedia, usePhoneLayout };
|
|
234
|
+
export { NOISE_CANCELLATION_EVENTS, excludeOsDefaultDevices, isChromiumBased, isFireFox, isLocal, isMacintosh, isSafari, playSound, playSoundOnDevice, playSpeakerTestTone, trackNoiseCancellationEvent, useFullScreen, useKeyPress, useMedia, usePhoneLayout };
|
|
231
235
|
//# sourceMappingURL=index.mjs.map
|
|
232
236
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/useFullScreen.ts","../src/useKeyPress.ts","../src/useMedia.ts","../src/usePhoneLayout.ts","../src/livekit.ts","../src/sounds.ts","../src/noiseCancellationTelemetry.ts"],"names":["useEffect","useState"],"mappings":"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","file":"index.mjs","sourcesContent":["import { useState, useEffect } from 'react';\n\nexport const useFullScreen = (containerId: string) => {\n const [isFullScreen, setIsFullScreen] = useState(false);\n\n const hasStandardFullscreen =\n typeof document !== 'undefined' &&\n document.fullscreenEnabled &&\n typeof HTMLElement.prototype.requestFullscreen === 'function';\n\n const isFullScreenSupported = hasStandardFullscreen;\n\n const toggleFullScreen = () => {\n const element = document.getElementById(containerId);\n const fullScreen = document.fullscreenElement;\n\n if (fullScreen) {\n document.exitFullscreen();\n setIsFullScreen(false);\n } else {\n element?.requestFullscreen();\n setIsFullScreen(true);\n }\n };\n\n useEffect(() => {\n if (!isFullScreenSupported) return;\n\n const handleFullScreenChange = () => {\n const fullScreen = !!document.fullscreenElement;\n setIsFullScreen(fullScreen);\n };\n\n document.addEventListener('fullscreenchange', handleFullScreenChange);\n\n return () => {\n document.removeEventListener('fullscreenchange', handleFullScreenChange);\n };\n }, [isFullScreenSupported]);\n\n return { toggleFullScreen, isFullScreen, isFullScreenSupported };\n};\n","import { useEffect } from 'react';\n\nexport const useKeyPress = (targetKey: string, callback: (event: KeyboardEvent) => void) => {\n useEffect(() => {\n // console.log(`Added listener for ${targetKey}`);\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === targetKey) {\n callback(event);\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => {\n // console.log(`Removed listener for ${targetKey}`);\n window.removeEventListener('keydown', handleKeyDown);\n };\n }, [targetKey, callback]);\n};\n","import { useEffect, useState } from 'react';\n\nconst isBrowser = typeof window !== 'undefined';\n\nconst getInitialState = (query: string, defaultState?: boolean) => {\n // Prevent a React hydration mismatch when a default value\n // is provided by not defaulting to window.matchMedia(query).matches.\n if (defaultState !== undefined) {\n return defaultState;\n }\n\n if (isBrowser) {\n return window.matchMedia(query).matches;\n }\n\n return false;\n};\n\nexport const useMedia = (query: string, defaultState?: boolean) => {\n const [state, setState] = useState(getInitialState(query, defaultState));\n\n useEffect(() => {\n let mounted = true;\n const mql = window.matchMedia(query);\n const onChange = () => {\n if (!mounted) {\n return;\n }\n setState(!!mql.matches);\n };\n\n mql.addEventListener('change', onChange);\n setState(mql.matches);\n\n return () => {\n mounted = false;\n mql.removeEventListener('change', onChange);\n };\n }, [query]);\n\n return state;\n};\n","import { useMedia } from './useMedia';\n\n/** Только телефон: планшеты и iPad используют десктопный compact-вид. */\nexport function usePhoneLayout(): boolean {\n return useMedia('(max-width: 720px)');\n}\n","import { getBrowser, LocalParticipant, LogLevel, Participant, setLogLevel } from 'livekit-client';\n\nexport const silenceLiveKitLogs = (shouldSilenceLogs: boolean) => {\n setLogLevel(shouldSilenceLogs ? LogLevel.silent : LogLevel.debug);\n};\n\nexport function isFireFox(): boolean {\n return getBrowser()?.name === 'Firefox';\n}\n\nexport function isChromiumBased(): boolean {\n return getBrowser()?.name === 'Chrome';\n}\n\nexport function isSafari(): boolean {\n return getBrowser()?.name === 'Safari';\n}\n\nexport function isLocal(p: Participant) {\n return p instanceof LocalParticipant;\n}\n\nexport function isMacintosh() {\n return navigator.platform.indexOf('Mac') > -1;\n}\n","/**\n * Утилита для воспроизведения звуков уведомлений в ВКС\n */\n\nconst SOUND_PATHS = {\n chatMessage: '/sounds/chat-message.wav',\n handRaise: '/sounds/hand-raise.wav',\n userJoin: '/sounds/user-join-to-call.mp3',\n userLeft: '/sounds/user-left-from-call.mp3',\n} as const;\n\ntype SoundType = keyof typeof SOUND_PATHS;\n\n/** Для join/left — воспроизводим только начало файла (мс). */\nconst JOIN_LEAVE_PLAY_MS = 500;\n\ntype TrimmedSoundType = Extract<SoundType, 'userJoin' | 'userLeft'>;\nconst trimTimeouts = new Map<TrimmedSoundType, ReturnType<typeof setTimeout>>();\n\n// Кэш для Audio объектов, чтобы не создавать их каждый раз\nconst soundCache = new Map<SoundType, HTMLAudioElement>();\n\n/**\n * Получает или создает Audio объект для звука\n */\nconst getAudio = (soundType: SoundType): HTMLAudioElement => {\n if (!soundCache.has(soundType)) {\n const audio = new Audio(SOUND_PATHS[soundType]);\n audio.preload = 'auto';\n soundCache.set(soundType, audio);\n }\n return soundCache.get(soundType)!;\n};\n\nconst clearJoinLeaveTrim = (soundType: TrimmedSoundType) => {\n const id = trimTimeouts.get(soundType);\n if (id !== undefined) {\n clearTimeout(id);\n trimTimeouts.delete(soundType);\n }\n};\n\nconst scheduleJoinLeaveTrim = (audio: HTMLAudioElement, soundType: TrimmedSoundType) => {\n clearJoinLeaveTrim(soundType);\n const id = setTimeout(() => {\n try {\n audio.pause();\n audio.currentTime = 0;\n } catch {\n /* ignore */\n }\n trimTimeouts.delete(soundType);\n }, JOIN_LEAVE_PLAY_MS);\n trimTimeouts.set(soundType, id);\n};\n\n/**\n * Воспроизводит звук с указанной громкостью\n * @param soundType - тип звука (chatMessage или handRaise)\n * @param volume - громкость от 0 до 1 (0 = беззвучно, 1 = максимальная громкость)\n */\nexport const playSound = (soundType: SoundType, volume: number = 1): void => {\n try {\n const audio = getAudio(soundType);\n\n if (soundType === 'userJoin' || soundType === 'userLeft') {\n clearJoinLeaveTrim(soundType);\n }\n\n // Устанавливаем громкость (от 0 до 1)\n const clampedVolume = Math.max(0, Math.min(1, volume));\n audio.volume = clampedVolume;\n\n // Сбрасываем позицию на начало и воспроизводим\n audio.currentTime = 0;\n void audio\n .play()\n .then(() => {\n if (soundType === 'userJoin' || soundType === 'userLeft') {\n scheduleJoinLeaveTrim(audio, soundType);\n }\n })\n .catch((error) => {\n // Игнорируем ошибки воспроизведения (например, если пользователь не взаимодействовал со страницей)\n console.warn(`⚠️ Failed to play sound ${soundType}:`, error);\n });\n } catch (error) {\n console.error(`❌ Error playing sound ${soundType}:`, error);\n }\n};\n\ntype PlaySoundOnDeviceOptions = {\n volume?: number;\n /** deviceId устройства вывода (HTMLMediaElement.setSinkId), если поддерживается */\n sinkId?: string;\n};\n\ntype AudioContextWithSink = AudioContext & {\n setSinkId?: (id: string) => Promise<void>;\n};\n\n/**\n * Воспроизводит тестовый звук на выбранном устройстве вывода.\n * Возвращает Promise, который резолвится после старта playback (или reject при ошибке).\n * Требует, чтобы хост отдавал файлы из `/sounds/...`.\n */\nexport const playSoundOnDevice = async (\n soundType: SoundType,\n options: PlaySoundOnDeviceOptions = {},\n): Promise<void> => {\n const { volume = 1, sinkId } = options;\n const audio = new Audio(SOUND_PATHS[soundType]);\n audio.volume = Math.max(0, Math.min(1, volume));\n\n const audioWithSink = audio as HTMLAudioElement & {\n setSinkId?: (id: string) => Promise<void>;\n };\n\n if (sinkId && typeof audioWithSink.setSinkId === 'function') {\n try {\n await audioWithSink.setSinkId(sinkId);\n } catch (error) {\n console.warn('⚠️ Failed to set audio output device:', error);\n }\n }\n\n audio.currentTime = 0;\n await audio.play();\n};\n\ntype PlaySpeakerTestToneOptions = {\n volume?: number;\n /** deviceId устройства вывода (AudioContext.setSinkId), если поддерживается */\n sinkId?: string;\n durationMs?: number;\n frequencyHz?: number;\n};\n\n/**\n * Тестовый тон через Web Audio API — не зависит от файлов `/sounds/*` на хосте.\n * Использует AudioContext.setSinkId, когда браузер и deviceId это позволяют.\n */\nexport const playSpeakerTestTone = async (\n options: PlaySpeakerTestToneOptions = {},\n): Promise<void> => {\n const { volume = 1, sinkId, durationMs = 900, frequencyHz = 880 } = options;\n\n const AudioContextCtor =\n window.AudioContext ||\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n\n if (!AudioContextCtor) {\n throw new Error('AudioContext is not supported');\n }\n\n const ctx = new AudioContextCtor() as AudioContextWithSink;\n\n try {\n if (sinkId && typeof ctx.setSinkId === 'function') {\n try {\n await ctx.setSinkId(sinkId);\n } catch (error) {\n console.warn('⚠️ Failed to set audio output device for test tone:', error);\n }\n }\n\n if (ctx.state === 'suspended') {\n await ctx.resume();\n }\n\n const oscillator = ctx.createOscillator();\n const gain = ctx.createGain();\n oscillator.type = 'sine';\n oscillator.frequency.value = frequencyHz;\n\n const peak = Math.max(0, Math.min(1, volume)) * 0.22;\n const now = ctx.currentTime;\n const end = now + durationMs / 1000;\n\n gain.gain.setValueAtTime(0, now);\n gain.gain.linearRampToValueAtTime(peak, now + 0.04);\n gain.gain.linearRampToValueAtTime(peak * 0.85, end - 0.12);\n gain.gain.linearRampToValueAtTime(0, end);\n\n oscillator.connect(gain);\n gain.connect(ctx.destination);\n oscillator.start(now);\n oscillator.stop(end);\n\n await new Promise<void>((resolve) => {\n window.setTimeout(resolve, durationMs + 40);\n });\n } finally {\n await ctx.close().catch(() => {\n /* ignore */\n });\n }\n};\n","/** События телеметрии шумоподавления для umami. */\nexport const NOISE_CANCELLATION_EVENTS = {\n MODE_CHANGED: 'noise_cancellation_mode_changed',\n APPLY_FAILED: 'noise_cancellation_apply_failed',\n FALLBACK_TO_WEBRTC: 'noise_cancellation_fallback_to_webrtc',\n KRISP_UNSUPPORTED_BROWSER: 'noise_cancellation_krisp_unsupported_browser',\n} as const;\n\ntype NoiseCancellationEvent =\n (typeof NOISE_CANCELLATION_EVENTS)[keyof typeof NOISE_CANCELLATION_EVENTS];\n\nfunction getUmami(): { track: (name: string, data?: Record<string, string>) => void } | undefined {\n if (typeof window === 'undefined') return undefined;\n const win = window as Window & {\n umami?: { track: (name: string, data?: Record<string, string>) => void };\n };\n return win.umami;\n}\n\nexport function trackNoiseCancellationEvent(\n event: NoiseCancellationEvent,\n data?: Record<string, string>,\n): void {\n const umami = getUmami();\n if (umami) {\n umami.track(event, data);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/useFullScreen.ts","../src/useKeyPress.ts","../src/useMedia.ts","../src/usePhoneLayout.ts","../src/livekit.ts","../src/sounds.ts","../src/noiseCancellationTelemetry.ts"],"names":["useEffect","useState"],"mappings":"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","file":"index.mjs","sourcesContent":["import { useState, useEffect } from 'react';\n\nexport const useFullScreen = (containerId: string) => {\n const [isFullScreen, setIsFullScreen] = useState(false);\n\n const hasStandardFullscreen =\n typeof document !== 'undefined' &&\n document.fullscreenEnabled &&\n typeof HTMLElement.prototype.requestFullscreen === 'function';\n\n const isFullScreenSupported = hasStandardFullscreen;\n\n const toggleFullScreen = () => {\n const element = document.getElementById(containerId);\n const fullScreen = document.fullscreenElement;\n\n if (fullScreen) {\n document.exitFullscreen();\n setIsFullScreen(false);\n } else {\n element?.requestFullscreen();\n setIsFullScreen(true);\n }\n };\n\n useEffect(() => {\n if (!isFullScreenSupported) return;\n\n const handleFullScreenChange = () => {\n const fullScreen = !!document.fullscreenElement;\n setIsFullScreen(fullScreen);\n };\n\n document.addEventListener('fullscreenchange', handleFullScreenChange);\n\n return () => {\n document.removeEventListener('fullscreenchange', handleFullScreenChange);\n };\n }, [isFullScreenSupported]);\n\n return { toggleFullScreen, isFullScreen, isFullScreenSupported };\n};\n","import { useEffect } from 'react';\n\nexport const useKeyPress = (targetKey: string, callback: (event: KeyboardEvent) => void) => {\n useEffect(() => {\n // console.log(`Added listener for ${targetKey}`);\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === targetKey) {\n callback(event);\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => {\n // console.log(`Removed listener for ${targetKey}`);\n window.removeEventListener('keydown', handleKeyDown);\n };\n }, [targetKey, callback]);\n};\n","import { useEffect, useState } from 'react';\n\nconst isBrowser = typeof window !== 'undefined';\n\nconst getInitialState = (query: string, defaultState?: boolean) => {\n // Prevent a React hydration mismatch when a default value\n // is provided by not defaulting to window.matchMedia(query).matches.\n if (defaultState !== undefined) {\n return defaultState;\n }\n\n if (isBrowser) {\n return window.matchMedia(query).matches;\n }\n\n return false;\n};\n\nexport const useMedia = (query: string, defaultState?: boolean) => {\n const [state, setState] = useState(getInitialState(query, defaultState));\n\n useEffect(() => {\n let mounted = true;\n const mql = window.matchMedia(query);\n const onChange = () => {\n if (!mounted) {\n return;\n }\n setState(!!mql.matches);\n };\n\n mql.addEventListener('change', onChange);\n setState(mql.matches);\n\n return () => {\n mounted = false;\n mql.removeEventListener('change', onChange);\n };\n }, [query]);\n\n return state;\n};\n","import { useMedia } from './useMedia';\n\n/** Только телефон: планшеты и iPad используют десктопный compact-вид. */\nexport function usePhoneLayout(): boolean {\n return useMedia('(max-width: 720px)');\n}\n","import { getBrowser, LocalParticipant, LogLevel, Participant, setLogLevel } from 'livekit-client';\n\nexport const silenceLiveKitLogs = (shouldSilenceLogs: boolean) => {\n setLogLevel(shouldSilenceLogs ? LogLevel.silent : LogLevel.debug);\n};\n\nexport function isFireFox(): boolean {\n return getBrowser()?.name === 'Firefox';\n}\n\nexport function isChromiumBased(): boolean {\n return getBrowser()?.name === 'Chrome';\n}\n\nexport function isSafari(): boolean {\n return getBrowser()?.name === 'Safari';\n}\n\nexport function isLocal(p: Participant) {\n return p instanceof LocalParticipant;\n}\n\nexport function isMacintosh() {\n return navigator.platform.indexOf('Mac') > -1;\n}\n\n/** Псевдо-id \"устройство ОС по умолчанию\" — не конкретное физическое устройство. */\nconst OS_DEFAULT_DEVICE_IDS = new Set(['default', 'communications']);\n\n/**\n * Браузер сам добавляет в `enumerateDevices()` псевдо-записи `\"default\"` (и\n * `\"communications\"` на Windows), которые указывают на РЕАЛЬНОЕ устройство,\n * уже присутствующее в том же списке под своим настоящим id — то есть одно и\n * то же физическое устройство пользователь видит дважды. Убираем эти\n * дубликаты: выбор конкретного устройства из списка достаточен, а сама\n * \"default\"-запись ещё и мешает надёжно определять активное устройство (см.\n * `useResolvedActiveDeviceId` — `activeDeviceId`/`track.getDeviceId()` иногда\n * тоже \"застревают\" на этом сентинеле, не называя конкретное устройство).\n */\nexport function excludeOsDefaultDevices(devices: MediaDeviceInfo[] | undefined): MediaDeviceInfo[] {\n return devices?.filter((device) => !OS_DEFAULT_DEVICE_IDS.has(device.deviceId)) ?? [];\n}\n","/**\n * Утилита для воспроизведения звуков уведомлений в ВКС\n */\n\nconst SOUND_PATHS = {\n chatMessage: '/sounds/chat-message.wav',\n handRaise: '/sounds/hand-raise.wav',\n userJoin: '/sounds/user-join-to-call.mp3',\n userLeft: '/sounds/user-left-from-call.mp3',\n} as const;\n\ntype SoundType = keyof typeof SOUND_PATHS;\n\n/** Для join/left — воспроизводим только начало файла (мс). */\nconst JOIN_LEAVE_PLAY_MS = 500;\n\ntype TrimmedSoundType = Extract<SoundType, 'userJoin' | 'userLeft'>;\nconst trimTimeouts = new Map<TrimmedSoundType, ReturnType<typeof setTimeout>>();\n\n// Кэш для Audio объектов, чтобы не создавать их каждый раз\nconst soundCache = new Map<SoundType, HTMLAudioElement>();\n\n/**\n * Получает или создает Audio объект для звука\n */\nconst getAudio = (soundType: SoundType): HTMLAudioElement => {\n if (!soundCache.has(soundType)) {\n const audio = new Audio(SOUND_PATHS[soundType]);\n audio.preload = 'auto';\n soundCache.set(soundType, audio);\n }\n return soundCache.get(soundType)!;\n};\n\nconst clearJoinLeaveTrim = (soundType: TrimmedSoundType) => {\n const id = trimTimeouts.get(soundType);\n if (id !== undefined) {\n clearTimeout(id);\n trimTimeouts.delete(soundType);\n }\n};\n\nconst scheduleJoinLeaveTrim = (audio: HTMLAudioElement, soundType: TrimmedSoundType) => {\n clearJoinLeaveTrim(soundType);\n const id = setTimeout(() => {\n try {\n audio.pause();\n audio.currentTime = 0;\n } catch {\n /* ignore */\n }\n trimTimeouts.delete(soundType);\n }, JOIN_LEAVE_PLAY_MS);\n trimTimeouts.set(soundType, id);\n};\n\n/**\n * Воспроизводит звук с указанной громкостью\n * @param soundType - тип звука (chatMessage или handRaise)\n * @param volume - громкость от 0 до 1 (0 = беззвучно, 1 = максимальная громкость)\n */\nexport const playSound = (soundType: SoundType, volume: number = 1): void => {\n try {\n const audio = getAudio(soundType);\n\n if (soundType === 'userJoin' || soundType === 'userLeft') {\n clearJoinLeaveTrim(soundType);\n }\n\n // Устанавливаем громкость (от 0 до 1)\n const clampedVolume = Math.max(0, Math.min(1, volume));\n audio.volume = clampedVolume;\n\n // Сбрасываем позицию на начало и воспроизводим\n audio.currentTime = 0;\n void audio\n .play()\n .then(() => {\n if (soundType === 'userJoin' || soundType === 'userLeft') {\n scheduleJoinLeaveTrim(audio, soundType);\n }\n })\n .catch((error) => {\n // Игнорируем ошибки воспроизведения (например, если пользователь не взаимодействовал со страницей)\n console.warn(`⚠️ Failed to play sound ${soundType}:`, error);\n });\n } catch (error) {\n console.error(`❌ Error playing sound ${soundType}:`, error);\n }\n};\n\ntype PlaySoundOnDeviceOptions = {\n volume?: number;\n /** deviceId устройства вывода (HTMLMediaElement.setSinkId), если поддерживается */\n sinkId?: string;\n};\n\ntype AudioContextWithSink = AudioContext & {\n setSinkId?: (id: string) => Promise<void>;\n};\n\n/**\n * Воспроизводит тестовый звук на выбранном устройстве вывода.\n * Возвращает Promise, который резолвится после старта playback (или reject при ошибке).\n * Требует, чтобы хост отдавал файлы из `/sounds/...`.\n */\nexport const playSoundOnDevice = async (\n soundType: SoundType,\n options: PlaySoundOnDeviceOptions = {},\n): Promise<void> => {\n const { volume = 1, sinkId } = options;\n const audio = new Audio(SOUND_PATHS[soundType]);\n audio.volume = Math.max(0, Math.min(1, volume));\n\n const audioWithSink = audio as HTMLAudioElement & {\n setSinkId?: (id: string) => Promise<void>;\n };\n\n if (sinkId && typeof audioWithSink.setSinkId === 'function') {\n try {\n await audioWithSink.setSinkId(sinkId);\n } catch (error) {\n console.warn('⚠️ Failed to set audio output device:', error);\n }\n }\n\n audio.currentTime = 0;\n await audio.play();\n};\n\ntype PlaySpeakerTestToneOptions = {\n volume?: number;\n /** deviceId устройства вывода (AudioContext.setSinkId), если поддерживается */\n sinkId?: string;\n durationMs?: number;\n frequencyHz?: number;\n};\n\n/**\n * Тестовый тон через Web Audio API — не зависит от файлов `/sounds/*` на хосте.\n * Использует AudioContext.setSinkId, когда браузер и deviceId это позволяют.\n */\nexport const playSpeakerTestTone = async (\n options: PlaySpeakerTestToneOptions = {},\n): Promise<void> => {\n const { volume = 1, sinkId, durationMs = 900, frequencyHz = 880 } = options;\n\n const AudioContextCtor =\n window.AudioContext ||\n (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;\n\n if (!AudioContextCtor) {\n throw new Error('AudioContext is not supported');\n }\n\n const ctx = new AudioContextCtor() as AudioContextWithSink;\n\n try {\n if (sinkId && typeof ctx.setSinkId === 'function') {\n try {\n await ctx.setSinkId(sinkId);\n } catch (error) {\n console.warn('⚠️ Failed to set audio output device for test tone:', error);\n }\n }\n\n if (ctx.state === 'suspended') {\n await ctx.resume();\n }\n\n const oscillator = ctx.createOscillator();\n const gain = ctx.createGain();\n oscillator.type = 'sine';\n oscillator.frequency.value = frequencyHz;\n\n const peak = Math.max(0, Math.min(1, volume)) * 0.22;\n const now = ctx.currentTime;\n const end = now + durationMs / 1000;\n\n gain.gain.setValueAtTime(0, now);\n gain.gain.linearRampToValueAtTime(peak, now + 0.04);\n gain.gain.linearRampToValueAtTime(peak * 0.85, end - 0.12);\n gain.gain.linearRampToValueAtTime(0, end);\n\n oscillator.connect(gain);\n gain.connect(ctx.destination);\n oscillator.start(now);\n oscillator.stop(end);\n\n await new Promise<void>((resolve) => {\n window.setTimeout(resolve, durationMs + 40);\n });\n } finally {\n await ctx.close().catch(() => {\n /* ignore */\n });\n }\n};\n","/** События телеметрии шумоподавления для umami. */\nexport const NOISE_CANCELLATION_EVENTS = {\n MODE_CHANGED: 'noise_cancellation_mode_changed',\n APPLY_FAILED: 'noise_cancellation_apply_failed',\n FALLBACK_TO_WEBRTC: 'noise_cancellation_fallback_to_webrtc',\n KRISP_UNSUPPORTED_BROWSER: 'noise_cancellation_krisp_unsupported_browser',\n} as const;\n\ntype NoiseCancellationEvent =\n (typeof NOISE_CANCELLATION_EVENTS)[keyof typeof NOISE_CANCELLATION_EVENTS];\n\nfunction getUmami(): { track: (name: string, data?: Record<string, string>) => void } | undefined {\n if (typeof window === 'undefined') return undefined;\n const win = window as Window & {\n umami?: { track: (name: string, data?: Record<string, string>) => void };\n };\n return win.umami;\n}\n\nexport function trackNoiseCancellationEvent(\n event: NoiseCancellationEvent,\n data?: Record<string, string>,\n): void {\n const umami = getUmami();\n if (umami) {\n umami.track(event, data);\n }\n}\n"]}
|