@siteed/expo-audio-studio 2.13.0 → 2.13.1

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js.map +1 -1
  3. package/build/cjs/AudioDeviceManager.js +4 -0
  4. package/build/cjs/AudioDeviceManager.js.map +1 -1
  5. package/build/cjs/WebRecorder.web.js +1 -0
  6. package/build/cjs/WebRecorder.web.js.map +1 -1
  7. package/build/cjs/useAudioRecorder.js +1 -0
  8. package/build/cjs/useAudioRecorder.js.map +1 -1
  9. package/build/cjs/workers/InlineFeaturesExtractor.web.js +8 -2
  10. package/build/cjs/workers/InlineFeaturesExtractor.web.js.map +1 -1
  11. package/build/esm/AudioAnalysis/AudioAnalysis.types.js.map +1 -1
  12. package/build/esm/AudioDeviceManager.js +4 -0
  13. package/build/esm/AudioDeviceManager.js.map +1 -1
  14. package/build/esm/WebRecorder.web.js +1 -0
  15. package/build/esm/WebRecorder.web.js.map +1 -1
  16. package/build/esm/useAudioRecorder.js +1 -0
  17. package/build/esm/useAudioRecorder.js.map +1 -1
  18. package/build/esm/workers/InlineFeaturesExtractor.web.js +8 -2
  19. package/build/esm/workers/InlineFeaturesExtractor.web.js.map +1 -1
  20. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts +1 -0
  21. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -1
  22. package/build/types/AudioDeviceManager.d.ts +4 -0
  23. package/build/types/AudioDeviceManager.d.ts.map +1 -1
  24. package/build/types/WebRecorder.web.d.ts.map +1 -1
  25. package/build/types/useAudioRecorder.d.ts.map +1 -1
  26. package/build/types/workers/InlineFeaturesExtractor.web.d.ts +1 -1
  27. package/build/types/workers/InlineFeaturesExtractor.web.d.ts.map +1 -1
  28. package/package.json +3 -4
  29. package/src/AudioAnalysis/AudioAnalysis.types.ts +1 -0
  30. package/src/AudioDeviceManager.ts +4 -0
  31. package/src/WebRecorder.web.ts +1 -0
  32. package/src/useAudioRecorder.tsx +1 -0
  33. package/src/workers/InlineFeaturesExtractor.web.tsx +8 -2
package/CHANGELOG.md CHANGED
@@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  ## [Unreleased]
9
9
 
10
10
 
11
+ ## [2.13.1] - 2025-06-09
12
+ ### Changed
13
+ - feat(investigation): resolve Issue #251 - comprehensive sub-100ms audio events analysis (#270) ([4813f1e](https://github.com/deeeed/expo-audio-stream/commit/4813f1ef05f3856b58ec8fde95b7b8909feb513d))
14
+ - fix(deps): update expo-modules-core peer dependency for Expo SDK 53 compatibility ([40b946f](https://github.com/deeeed/expo-audio-stream/commit/40b946f83eecd3fdcedfe7a2cbac62f1207a4ff0))
15
+ - docs: updated docs site ([8a01a97](https://github.com/deeeed/expo-audio-stream/commit/8a01a97ebee927a2dfa0a7cb40b11329410509d2))
11
16
  ## [2.13.0] - 2025-06-09
12
17
  ### Changed
13
18
  - feat(expo-audio-studio): enhance device detection and management system ([97ceef0](https://github.com/deeeed/expo-audio-stream/commit/97ceef003ddb8eb5246cda8a5a00ddc75bf665a0))
@@ -307,7 +312,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
307
312
  - Feature: Audio features extraction during recording.
308
313
  - Feature: Consistent WAV PCM recording format across all platforms.
309
314
 
310
- [unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.13.0...HEAD
315
+ [unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.13.1...HEAD
316
+ [2.13.1]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.13.0...@siteed/expo-audio-studio@2.13.1
311
317
  [2.13.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.12.3...@siteed/expo-audio-studio@2.13.0
312
318
  [2.12.3]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.12.2...@siteed/expo-audio-studio@2.12.3
313
319
  [2.12.2]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.12.1...@siteed/expo-audio-studio@2.12.2
@@ -1 +1 @@
1
- {"version":3,"file":"AudioAnalysis.types.js","sourceRoot":"","sources":["../../../src/AudioAnalysis/AudioAnalysis.types.ts"],"names":[],"mappings":";AAAA,sEAAsE","sourcesContent":["// packages/expo-audio-stream/src/AudioAnalysis/AudioAnalysis.types.ts\n\nimport { BitDepth, ConsoleLike } from '../ExpoAudioStream.types'\n\n/**\n * Represents the configuration for decoding audio data.\n */\nexport interface DecodingConfig {\n /** Target sample rate for decoded audio (Android and Web) */\n targetSampleRate?: number\n /** Target number of channels (Android and Web) */\n targetChannels?: number\n /** Target bit depth (Android and Web) */\n targetBitDepth?: BitDepth\n /** Whether to normalize audio levels (Android and Web) */\n normalizeAudio?: boolean\n}\n\n/**\n * Represents speech-related features extracted from audio.\n */\nexport interface SpeechFeatures {\n isActive: boolean // Whether speech is detected in this segment\n speakerId?: number // Optional speaker identification\n // Could add more speech-related features here like:\n // confidence: number\n // language?: string\n // sentiment?: number\n // etc.\n}\n\n/**\n * Represents various audio features extracted from an audio signal.\n */\nexport interface AudioFeatures {\n energy?: number // The infinite integral of the squared signal, representing the overall energy of the audio.\n mfcc?: number[] // Mel-frequency cepstral coefficients, describing the short-term power spectrum of a sound.\n rms?: number // Root mean square value, indicating the amplitude of the audio signal.\n minAmplitude?: number // Minimum amplitude value in the audio signal.\n maxAmplitude?: number // Maximum amplitude value in the audio signal.\n zcr?: number // Zero-crossing rate, indicating the rate at which the signal changes sign.\n spectralCentroid?: number // The center of mass of the spectrum, indicating the brightness of the sound.\n spectralFlatness?: number // Measure of the flatness of the spectrum, indicating how noise-like the signal is.\n spectralRolloff?: number // The frequency below which a specified percentage (usually 85%) of the total spectral energy lies.\n spectralBandwidth?: number // The width of the spectrum, indicating the range of frequencies present.\n chromagram?: number[] // Chromagram, representing the 12 different pitch classes of the audio.\n tempo?: number // Estimated tempo of the audio signal, measured in beats per minute (BPM).\n hnr?: number // Harmonics-to-noise ratio, indicating the proportion of harmonics to noise in the audio signal.\n melSpectrogram?: number[] // Mel-scaled spectrogram representation of the audio.\n spectralContrast?: number[] // Spectral contrast features representing the difference between peaks and valleys.\n tonnetz?: number[] // Tonal network features representing harmonic relationships.\n pitch?: number // Pitch of the audio signal, measured in Hertz (Hz).\n crc32?: number // crc32 checksum of the audio signal, used to verify the integrity of the audio.\n}\n\n/**\n * Options to specify which audio features to extract.\n * Note: Advanced features (spectral features, chromagram, pitch, etc.) are experimental,\n * especially during live recording, due to high processing requirements.\n */\nexport interface AudioFeaturesOptions {\n // Basic features - well optimized\n energy?: boolean\n rms?: boolean\n zcr?: boolean\n\n // Advanced features - experimental, may impact performance in live recording\n mfcc?: boolean\n spectralCentroid?: boolean\n spectralFlatness?: boolean\n spectralRolloff?: boolean\n spectralBandwidth?: boolean\n chromagram?: boolean\n tempo?: boolean\n hnr?: boolean\n melSpectrogram?: boolean\n spectralContrast?: boolean\n tonnetz?: boolean\n pitch?: boolean\n\n // Utility\n crc32?: boolean\n}\n\n/**\n * Represents a single data point in the audio analysis.\n */\nexport interface DataPoint {\n id: number\n amplitude: number // Peak amplitude for the segment\n rms: number // Root mean square value\n dB: number // dBFS (decibels relative to full scale) computed from RMS value\n silent: boolean // Always computed\n features?: AudioFeatures\n speech?: SpeechFeatures\n startTime?: number\n endTime?: number\n // start / end position in bytes\n startPosition?: number\n endPosition?: number\n // number of audio samples for this point (samples size depends on bit depth)\n samples?: number\n}\n\n/**\n * Represents the complete data from the audio analysis.\n */\nexport interface AudioAnalysis {\n segmentDurationMs: number // Duration of each segment in milliseconds\n durationMs: number // Duration of the audio in milliseconds\n bitDepth: number // Bit depth of the audio\n samples: number // Size of the audio in bytes\n numberOfChannels: number // Number of audio channels\n sampleRate: number // Sample rate of the audio\n dataPoints: DataPoint[] // Array of data points from the analysis.\n amplitudeRange: {\n min: number\n max: number\n }\n rmsRange: {\n min: number\n max: number\n }\n // TODO: speaker changes into a broader speech analysis section\n speechAnalysis?: {\n speakerChanges: {\n timestamp: number\n speakerId: number\n }[]\n // Could add more speech analysis data here like:\n // dominantSpeaker?: number\n // totalSpeechDuration?: number\n // speakerStats?: { [speakerId: number]: { duration: number, segments: number } }\n }\n}\n\n/**\n * Options for specifying a time range within an audio file.\n */\nexport interface AudioRangeOptions {\n /** Start time in milliseconds */\n startTimeMs?: number\n /** End time in milliseconds */\n endTimeMs?: number\n}\n\n/**\n * Options for generating a quick preview of audio waveform.\n * This is optimized for UI rendering with a specified number of points.\n */\nexport interface PreviewOptions extends AudioRangeOptions {\n /** URI of the audio file to analyze */\n fileUri: string\n /**\n * Total number of points to generate for the preview.\n * @default 100\n */\n numberOfPoints?: number\n /**\n * Optional logger for debugging.\n */\n logger?: ConsoleLike\n /**\n * Optional configuration for decoding the audio file.\n * Defaults to:\n * - targetSampleRate: undefined (keep original)\n * - targetChannels: undefined (keep original)\n * - targetBitDepth: 16\n * - normalizeAudio: false\n */\n decodingOptions?: DecodingConfig\n}\n\n/**\n * Options for mel-spectrogram extraction\n *\n * @experimental This feature is experimental and currently only available on Android.\n * The API may change in future versions.\n */\nexport interface ExtractMelSpectrogramOptions {\n fileUri?: string // Path to audio file\n arrayBuffer?: ArrayBuffer // Raw audio buffer\n windowSizeMs: number // Window size in ms (e.g., 25)\n hopLengthMs: number // Hop length in ms (e.g., 10)\n nMels: number // Number of mel filters (e.g., 60)\n fMin?: number // Min frequency (default: 0)\n fMax?: number // Max frequency (default: sampleRate / 2)\n windowType?: 'hann' | 'hamming' // Window function (default: 'hann')\n normalize?: boolean // Mean normalization (default: false)\n logScale?: boolean // Log scaling of mel energies (default: true)\n decodingOptions?: DecodingConfig // Audio decoding settings\n startTimeMs?: number // Optional start time\n endTimeMs?: number // Optional end time\n logger?: ConsoleLike\n}\n\n/**\n * Return type for mel spectrogram extraction\n *\n * @experimental This feature is experimental and currently only available on Android.\n * The API may change in future versions.\n */\nexport interface MelSpectrogram {\n spectrogram: number[][] // 2D array [time][mel]\n sampleRate: number // Audio sample rate\n nMels: number // Number of mel filters\n timeSteps: number // Number of time frames\n durationMs: number // Audio duration in ms\n}\n"]}
1
+ {"version":3,"file":"AudioAnalysis.types.js","sourceRoot":"","sources":["../../../src/AudioAnalysis/AudioAnalysis.types.ts"],"names":[],"mappings":";AAAA,sEAAsE","sourcesContent":["// packages/expo-audio-stream/src/AudioAnalysis/AudioAnalysis.types.ts\n\nimport { BitDepth, ConsoleLike } from '../ExpoAudioStream.types'\n\n/**\n * Represents the configuration for decoding audio data.\n */\nexport interface DecodingConfig {\n /** Target sample rate for decoded audio (Android and Web) */\n targetSampleRate?: number\n /** Target number of channels (Android and Web) */\n targetChannels?: number\n /** Target bit depth (Android and Web) */\n targetBitDepth?: BitDepth\n /** Whether to normalize audio levels (Android and Web) */\n normalizeAudio?: boolean\n}\n\n/**\n * Represents speech-related features extracted from audio.\n */\nexport interface SpeechFeatures {\n isActive: boolean // Whether speech is detected in this segment\n speakerId?: number // Optional speaker identification\n // Could add more speech-related features here like:\n // confidence: number\n // language?: string\n // sentiment?: number\n // etc.\n}\n\n/**\n * Represents various audio features extracted from an audio signal.\n */\nexport interface AudioFeatures {\n energy?: number // The infinite integral of the squared signal, representing the overall energy of the audio.\n mfcc?: number[] // Mel-frequency cepstral coefficients, describing the short-term power spectrum of a sound.\n rms?: number // Root mean square value, indicating the amplitude of the audio signal.\n minAmplitude?: number // Minimum amplitude value in the audio signal.\n maxAmplitude?: number // Maximum amplitude value in the audio signal.\n zcr?: number // Zero-crossing rate, indicating the rate at which the signal changes sign.\n spectralCentroid?: number // The center of mass of the spectrum, indicating the brightness of the sound.\n spectralFlatness?: number // Measure of the flatness of the spectrum, indicating how noise-like the signal is.\n spectralRolloff?: number // The frequency below which a specified percentage (usually 85%) of the total spectral energy lies.\n spectralBandwidth?: number // The width of the spectrum, indicating the range of frequencies present.\n chromagram?: number[] // Chromagram, representing the 12 different pitch classes of the audio.\n tempo?: number // Estimated tempo of the audio signal, measured in beats per minute (BPM).\n hnr?: number // Harmonics-to-noise ratio, indicating the proportion of harmonics to noise in the audio signal.\n melSpectrogram?: number[] // Mel-scaled spectrogram representation of the audio.\n spectralContrast?: number[] // Spectral contrast features representing the difference between peaks and valleys.\n tonnetz?: number[] // Tonal network features representing harmonic relationships.\n pitch?: number // Pitch of the audio signal, measured in Hertz (Hz).\n crc32?: number // crc32 checksum of the audio signal, used to verify the integrity of the audio.\n}\n\n/**\n * Options to specify which audio features to extract.\n * Note: Advanced features (spectral features, chromagram, pitch, etc.) are experimental,\n * especially during live recording, due to high processing requirements.\n */\nexport interface AudioFeaturesOptions {\n // Basic features - well optimized\n energy?: boolean\n rms?: boolean\n zcr?: boolean\n\n // Advanced features - experimental, may impact performance in live recording\n mfcc?: boolean\n spectralCentroid?: boolean\n spectralFlatness?: boolean\n spectralRolloff?: boolean\n spectralBandwidth?: boolean\n chromagram?: boolean\n tempo?: boolean\n hnr?: boolean\n melSpectrogram?: boolean\n spectralContrast?: boolean\n tonnetz?: boolean\n pitch?: boolean\n\n // Utility\n crc32?: boolean\n}\n\n/**\n * Represents a single data point in the audio analysis.\n */\nexport interface DataPoint {\n id: number\n amplitude: number // Peak amplitude for the segment\n rms: number // Root mean square value\n dB: number // dBFS (decibels relative to full scale) computed from RMS value\n silent: boolean // Always computed\n features?: AudioFeatures\n speech?: SpeechFeatures\n startTime?: number\n endTime?: number\n // start / end position in bytes\n startPosition?: number\n endPosition?: number\n // number of audio samples for this point (samples size depends on bit depth)\n samples?: number\n}\n\n/**\n * Represents the complete data from the audio analysis.\n */\nexport interface AudioAnalysis {\n segmentDurationMs: number // Duration of each segment in milliseconds\n durationMs: number // Duration of the audio in milliseconds\n bitDepth: number // Bit depth of the audio\n samples: number // Size of the audio in bytes\n numberOfChannels: number // Number of audio channels\n sampleRate: number // Sample rate of the audio\n dataPoints: DataPoint[] // Array of data points from the analysis.\n amplitudeRange: {\n min: number\n max: number\n }\n rmsRange: {\n min: number\n max: number\n }\n extractionTimeMs: number // Time taken to extract/process the analysis in milliseconds\n // TODO: speaker changes into a broader speech analysis section\n speechAnalysis?: {\n speakerChanges: {\n timestamp: number\n speakerId: number\n }[]\n // Could add more speech analysis data here like:\n // dominantSpeaker?: number\n // totalSpeechDuration?: number\n // speakerStats?: { [speakerId: number]: { duration: number, segments: number } }\n }\n}\n\n/**\n * Options for specifying a time range within an audio file.\n */\nexport interface AudioRangeOptions {\n /** Start time in milliseconds */\n startTimeMs?: number\n /** End time in milliseconds */\n endTimeMs?: number\n}\n\n/**\n * Options for generating a quick preview of audio waveform.\n * This is optimized for UI rendering with a specified number of points.\n */\nexport interface PreviewOptions extends AudioRangeOptions {\n /** URI of the audio file to analyze */\n fileUri: string\n /**\n * Total number of points to generate for the preview.\n * @default 100\n */\n numberOfPoints?: number\n /**\n * Optional logger for debugging.\n */\n logger?: ConsoleLike\n /**\n * Optional configuration for decoding the audio file.\n * Defaults to:\n * - targetSampleRate: undefined (keep original)\n * - targetChannels: undefined (keep original)\n * - targetBitDepth: 16\n * - normalizeAudio: false\n */\n decodingOptions?: DecodingConfig\n}\n\n/**\n * Options for mel-spectrogram extraction\n *\n * @experimental This feature is experimental and currently only available on Android.\n * The API may change in future versions.\n */\nexport interface ExtractMelSpectrogramOptions {\n fileUri?: string // Path to audio file\n arrayBuffer?: ArrayBuffer // Raw audio buffer\n windowSizeMs: number // Window size in ms (e.g., 25)\n hopLengthMs: number // Hop length in ms (e.g., 10)\n nMels: number // Number of mel filters (e.g., 60)\n fMin?: number // Min frequency (default: 0)\n fMax?: number // Max frequency (default: sampleRate / 2)\n windowType?: 'hann' | 'hamming' // Window function (default: 'hann')\n normalize?: boolean // Mean normalization (default: false)\n logScale?: boolean // Log scaling of mel energies (default: true)\n decodingOptions?: DecodingConfig // Audio decoding settings\n startTimeMs?: number // Optional start time\n endTimeMs?: number // Optional end time\n logger?: ConsoleLike\n}\n\n/**\n * Return type for mel spectrogram extraction\n *\n * @experimental This feature is experimental and currently only available on Android.\n * The API may change in future versions.\n */\nexport interface MelSpectrogram {\n spectrogram: number[][] // 2D array [time][mel]\n sampleRate: number // Audio sample rate\n nMels: number // Number of mel filters\n timeSteps: number // Number of time frames\n durationMs: number // Audio duration in ms\n}\n"]}
@@ -52,18 +52,22 @@ function mapRawDeviceToAudioDevice(rawDevice) {
52
52
  * ========================
53
53
  *
54
54
  * Device Events (deviceChangedEvent):
55
+ * ```
55
56
  * {
56
57
  * type: "deviceConnected" | "deviceDisconnected",
57
58
  * deviceId: string
58
59
  * }
60
+ * ```
59
61
  *
60
62
  * Recording Interruption Events (recordingInterruptedEvent):
63
+ * ```
61
64
  * {
62
65
  * reason: "userPaused" | "userResumed" | "audioFocusLoss" | "audioFocusGain" |
63
66
  * "deviceFallback" | "deviceSwitchFailed" | "phoneCall" | "phoneCallEnded",
64
67
  * isPaused: boolean,
65
68
  * timestamp: number
66
69
  * }
70
+ * ```
67
71
  *
68
72
  * NOTE: Device events use "type" field, interruption events use "reason" field.
69
73
  * This is intentional to distinguish between different event categories.
@@ -1 +1 @@
1
- {"version":3,"file":"AudioDeviceManager.js","sourceRoot":"","sources":["../../src/AudioDeviceManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventEmitter } from 'expo-modules-core'\nimport { Platform } from 'react-native'\n\nimport {\n AudioDevice,\n AudioDeviceCapabilities,\n DeviceDisconnectionBehavior,\n ConsoleLike,\n} from './ExpoAudioStream.types'\nimport ExpoAudioStreamModule from './ExpoAudioStreamModule'\n\n// Default device fallback for web and unsupported platforms\nconst DEFAULT_DEVICE: AudioDevice = {\n id: 'default',\n name: 'Default Microphone',\n type: 'builtin_mic',\n isDefault: true,\n isAvailable: true,\n capabilities: {\n sampleRates: [16000, 44100, 48000],\n channelCounts: [1, 2],\n bitDepths: [16, 24, 32],\n hasEchoCancellation: true,\n hasNoiseSuppression: true,\n hasAutomaticGainControl: true,\n },\n}\n\n// Helper function to map raw object to AudioDevice interface\n// This handles potential inconsistencies from the native module\nfunction mapRawDeviceToAudioDevice(rawDevice: any): AudioDevice {\n const capabilities = rawDevice.capabilities || {}\n return {\n id: rawDevice.id || 'unknown',\n name: rawDevice.name || 'Unknown Device',\n type: rawDevice.type || 'unknown',\n isDefault: rawDevice.isDefault || false,\n isAvailable:\n rawDevice.isAvailable !== undefined ? rawDevice.isAvailable : true, // Default to true if undefined\n capabilities: {\n sampleRates: capabilities.sampleRates || [16000, 44100, 48000], // Provide defaults\n channelCounts: capabilities.channelCounts || [1, 2],\n bitDepths: capabilities.bitDepths || [16, 24, 32],\n hasEchoCancellation: capabilities.hasEchoCancellation,\n hasNoiseSuppression: capabilities.hasNoiseSuppression,\n hasAutomaticGainControl: capabilities.hasAutomaticGainControl,\n },\n }\n}\n\n/**\n * Class that provides a cross-platform API for managing audio input devices\n *\n * EVENT API SPECIFICATION:\n * ========================\n *\n * Device Events (deviceChangedEvent):\n * {\n * type: \"deviceConnected\" | \"deviceDisconnected\",\n * deviceId: string\n * }\n *\n * Recording Interruption Events (recordingInterruptedEvent):\n * {\n * reason: \"userPaused\" | \"userResumed\" | \"audioFocusLoss\" | \"audioFocusGain\" |\n * \"deviceFallback\" | \"deviceSwitchFailed\" | \"phoneCall\" | \"phoneCallEnded\",\n * isPaused: boolean,\n * timestamp: number\n * }\n *\n * NOTE: Device events use \"type\" field, interruption events use \"reason\" field.\n * This is intentional to distinguish between different event categories.\n */\nexport class AudioDeviceManager {\n private eventEmitter: InstanceType<typeof EventEmitter>\n private currentDeviceId: string | null = null\n private availableDevices: AudioDevice[] = []\n private deviceChangeListeners: Set<(devices: AudioDevice[]) => void> =\n new Set()\n private webDeviceChangeHandler?: () => void\n private lastRefreshTime: number = 0\n private refreshInProgress: boolean = false\n private refreshDebounceMs: number = 500 // Minimum 500ms between refreshes\n private logger?: ConsoleLike\n\n // Track temporarily disconnected devices\n private temporarilyDisconnectedDevices: Set<string> = new Set()\n private disconnectionTimeouts: Map<string, NodeJS.Timeout> = new Map()\n private readonly DISCONNECTION_TIMEOUT_MS = 5000 // 5 seconds\n\n constructor(options?: { logger?: ConsoleLike }) {\n this.eventEmitter = new EventEmitter(ExpoAudioStreamModule)\n this.logger = options?.logger\n\n // Set up device event listeners for all platforms immediately\n this.setupDeviceEventListeners()\n }\n\n /**\n * Set up device event listeners for the current platform\n */\n private setupDeviceEventListeners(): void {\n if (Platform.OS === 'web') {\n this.setupWebDeviceChangeListener()\n } else {\n this.setupNativeDeviceEventListener()\n }\n }\n\n /**\n * Set up native device event listener for iOS/Android\n */\n private setupNativeDeviceEventListener(): void {\n // Store the last event type to avoid duplicates\n let lastEventType: string | null = null\n let lastEventTime = 0\n\n this.eventEmitter.addListener('deviceChangedEvent', (event: any) => {\n // Skip processing duplicate events that occur too close together\n const now = Date.now()\n const isSimilarEvent =\n lastEventType === event.type &&\n now - lastEventTime < this.refreshDebounceMs\n\n if (isSimilarEvent) {\n this.logger?.debug(\n `Skipping similar device event (${event.type}) received too soon`\n )\n return\n }\n\n // Update the last event tracking\n lastEventType = event.type\n lastEventTime = now\n\n // Only refresh on meaningful events\n if (\n event.type === 'deviceConnected' ||\n event.type === 'deviceDisconnected' ||\n event.type === 'routeChanged'\n ) {\n this.logger?.debug(`Processing device event: ${event.type}`)\n // Force refresh for device events to ensure fresh data\n this.forceRefreshDevices()\n }\n })\n this.logger?.debug('Native device event listener set up')\n }\n\n /**\n * Initialize the device manager with a logger\n * @param logger A logger instance that implements the ConsoleLike interface\n * @returns The manager instance for chaining\n */\n initWithLogger(logger: ConsoleLike): AudioDeviceManager {\n this.setLogger(logger)\n return this\n }\n\n /**\n * Set the logger instance\n * @param logger A logger instance that implements the ConsoleLike interface\n */\n setLogger(logger: ConsoleLike) {\n this.logger = logger\n }\n\n /**\n * Initialize or reinitialize device detection\n * Useful for restarting device detection if initial setup failed\n */\n initializeDeviceDetection(): void {\n this.logger?.debug('Initializing device detection...')\n\n // Clean up existing listeners first\n if (Platform.OS === 'web' && this.webDeviceChangeHandler) {\n if (typeof navigator !== 'undefined' && navigator.mediaDevices) {\n navigator.mediaDevices.removeEventListener(\n 'devicechange',\n this.webDeviceChangeHandler\n )\n }\n this.webDeviceChangeHandler = undefined\n }\n\n // Re-setup device event listeners\n this.setupDeviceEventListeners()\n }\n\n /**\n * Get the current logger instance\n * @returns The logger instance or undefined if not set\n */\n getLogger(): ConsoleLike | undefined {\n return this.logger\n }\n\n /**\n * Get all available audio input devices\n * @param options Optional settings to force refresh the device list. Can include a refresh flag.\n * @returns Promise resolving to an array of audio devices conforming to AudioDevice interface\n */\n async getAvailableDevices(options?: {\n refresh?: boolean\n }): Promise<AudioDevice[]> {\n try {\n if (Platform.OS === 'web') {\n this.availableDevices = await this.getWebAudioDevices()\n } else if (ExpoAudioStreamModule.getAvailableInputDevices) {\n // Expecting an array of raw device objects from native\n const rawDevices: any[] =\n await ExpoAudioStreamModule.getAvailableInputDevices(\n options\n )\n // Map raw objects to the AudioDevice interface\n this.availableDevices = rawDevices.map(\n mapRawDeviceToAudioDevice\n )\n } else {\n // Fallback for unsupported platforms\n this.availableDevices = [DEFAULT_DEVICE]\n }\n return this.availableDevices\n } catch (error) {\n this.logger?.error('Failed to get available devices:', error)\n this.availableDevices = [DEFAULT_DEVICE] // Ensure state is updated on error\n return this.availableDevices\n }\n }\n\n /**\n * Get the currently selected audio input device\n * @returns Promise resolving to the current device (conforming to AudioDevice) or null\n */\n async getCurrentDevice(): Promise<AudioDevice | null> {\n try {\n if (Platform.OS === 'web') {\n if (!this.currentDeviceId) {\n // On web, return the typed default device if nothing is selected\n return DEFAULT_DEVICE\n }\n // Refresh web devices to ensure the current one is up-to-date\n const webDevices = await this.getWebAudioDevices()\n return (\n webDevices.find((d) => d.id === this.currentDeviceId) ||\n DEFAULT_DEVICE // Fallback to default if current ID not found\n )\n } else if (ExpoAudioStreamModule.getCurrentInputDevice) {\n // Expecting a single raw device object or null from native\n const rawDevice: any | null =\n await ExpoAudioStreamModule.getCurrentInputDevice()\n // Map to AudioDevice interface if not null\n return rawDevice ? mapRawDeviceToAudioDevice(rawDevice) : null\n } else {\n // Fallback for unsupported platforms\n return DEFAULT_DEVICE\n }\n } catch (error) {\n this.logger?.error('Failed to get current device:', error)\n return DEFAULT_DEVICE // Return default on error\n }\n }\n\n /**\n * Select a specific audio input device for recording\n * @param deviceId The ID of the device to select\n * @returns Promise resolving to a boolean indicating success\n */\n async selectDevice(deviceId: string): Promise<boolean> {\n try {\n let success = false\n if (Platform.OS === 'web') {\n // Check if the device exists before setting it\n const devices = await this.getWebAudioDevices()\n if (devices.some((d) => d.id === deviceId)) {\n this.currentDeviceId = deviceId\n success = true\n } else {\n this.logger?.warn(\n `Web: Device with ID ${deviceId} not found.`\n )\n success = false\n }\n } else if (ExpoAudioStreamModule.selectInputDevice) {\n success =\n await ExpoAudioStreamModule.selectInputDevice(deviceId)\n if (success) {\n this.currentDeviceId = deviceId\n }\n }\n // Refresh devices after selection attempt to update state\n await this.refreshDevices()\n return success\n } catch (error) {\n this.logger?.error('Failed to select device:', error)\n await this.refreshDevices() // Refresh even on error\n return false\n }\n }\n\n /**\n * Reset to the default audio input device\n * @returns Promise resolving to a boolean indicating success\n */\n async resetToDefaultDevice(): Promise<boolean> {\n try {\n let success = false\n if (Platform.OS === 'web') {\n this.currentDeviceId = 'default'\n success = true\n } else if (ExpoAudioStreamModule.resetToDefaultDevice) {\n success = await ExpoAudioStreamModule.resetToDefaultDevice()\n if (success) {\n this.currentDeviceId = null\n }\n }\n // Refresh devices after reset attempt\n await this.refreshDevices()\n return success\n } catch (error) {\n this.logger?.error('Failed to reset to default device:', error)\n await this.refreshDevices() // Refresh even on error\n return false\n }\n }\n\n /**\n * Register a listener for device changes\n * @param listener Function to call when devices change (receives AudioDevice[])\n * @returns Function to remove the listener\n */\n addDeviceChangeListener(\n listener: (devices: AudioDevice[]) => void\n ): () => void {\n this.deviceChangeListeners.add(listener)\n\n // Immediately call listener with current devices if available\n if (this.availableDevices.length > 0) {\n listener([...this.availableDevices])\n }\n\n // Return a function to remove the listener\n return () => {\n this.deviceChangeListeners.delete(listener)\n }\n }\n\n /**\n * Mark a device as temporarily disconnected (for UI filtering)\n * @param deviceId The ID of the device that was disconnected\n * @param notify Whether to notify listeners immediately (default: true)\n */\n markDeviceAsDisconnected(deviceId: string, notify: boolean = true): void {\n this.logger?.debug(\n `Marking device ${deviceId} as temporarily disconnected`\n )\n\n // Clear any existing timeout for this device\n const existingTimeout = this.disconnectionTimeouts.get(deviceId)\n if (existingTimeout) {\n clearTimeout(existingTimeout)\n }\n\n // Add to disconnected set\n this.temporarilyDisconnectedDevices.add(deviceId)\n\n // Set timeout to remove from disconnected set\n const timeout = setTimeout(() => {\n this.logger?.debug(\n `Reconnection timeout expired for device ${deviceId}`\n )\n this.temporarilyDisconnectedDevices.delete(deviceId)\n this.disconnectionTimeouts.delete(deviceId)\n // Refresh devices to show the device again if it's still available\n this.forceRefreshDevices()\n }, this.DISCONNECTION_TIMEOUT_MS)\n\n this.disconnectionTimeouts.set(deviceId, timeout)\n\n // Only notify listeners if requested\n if (notify) {\n this.notifyListeners()\n }\n }\n\n /**\n * Mark a device as reconnected (remove from disconnected set)\n * @param deviceId The ID of the device that was reconnected\n */\n markDeviceAsReconnected(deviceId: string): void {\n this.logger?.debug(`Marking device ${deviceId} as reconnected`)\n\n // Clear timeout and remove from disconnected set\n const timeout = this.disconnectionTimeouts.get(deviceId)\n if (timeout) {\n clearTimeout(timeout)\n this.disconnectionTimeouts.delete(deviceId)\n }\n\n this.temporarilyDisconnectedDevices.delete(deviceId)\n\n // Notify listeners with updated device list\n this.notifyListeners()\n }\n\n /**\n * Get filtered device list (excluding temporarily disconnected devices)\n * @returns Array of available devices excluding temporarily disconnected ones\n */\n private getFilteredDevices(): AudioDevice[] {\n if (this.temporarilyDisconnectedDevices.size === 0) {\n return [...this.availableDevices]\n }\n\n const filtered = this.availableDevices.filter(\n (device) => !this.temporarilyDisconnectedDevices.has(device.id)\n )\n\n this.logger?.debug(\n `Filtered ${this.availableDevices.length - filtered.length} temporarily disconnected devices. ` +\n `Showing ${filtered.length} devices.`\n )\n\n return filtered\n }\n\n /**\n * Get the raw device list (including temporarily disconnected devices)\n * @returns Array of all available devices from native layer\n */\n getRawDevices(): AudioDevice[] {\n return [...this.availableDevices]\n }\n\n /**\n * Get the IDs of temporarily disconnected devices\n * @returns Set of device IDs that are temporarily hidden from UI\n */\n getTemporarilyDisconnectedDeviceIds(): ReadonlySet<string> {\n return new Set(this.temporarilyDisconnectedDevices)\n }\n\n /**\n * Clean up timeouts and listeners (useful for testing or cleanup)\n */\n cleanup(): void {\n // Clear all disconnection timeouts\n this.disconnectionTimeouts.forEach((timeout) => clearTimeout(timeout))\n this.disconnectionTimeouts.clear()\n this.temporarilyDisconnectedDevices.clear()\n\n // Clear device change listeners\n this.deviceChangeListeners.clear()\n\n // Clean up web device listener\n if (Platform.OS === 'web' && this.webDeviceChangeHandler) {\n if (typeof navigator !== 'undefined' && navigator.mediaDevices) {\n navigator.mediaDevices.removeEventListener(\n 'devicechange',\n this.webDeviceChangeHandler\n )\n }\n this.webDeviceChangeHandler = undefined\n }\n\n this.logger?.debug('AudioDeviceManager cleanup completed')\n }\n\n /**\n * Force refresh devices without debouncing (for device events)\n * @returns Promise resolving to the updated device list (AudioDevice[])\n */\n async forceRefreshDevices(): Promise<AudioDevice[]> {\n this.logger?.debug('Force refreshing devices (bypassing debounce)...')\n this.refreshInProgress = true\n try {\n // Force fetch the latest devices from native layer\n const devices = await this.getAvailableDevices({ refresh: true })\n // Update internal state\n this.availableDevices = devices\n // Notify listeners with fresh data\n this.notifyListeners()\n this.lastRefreshTime = Date.now()\n return devices\n } catch (error) {\n this.logger?.error('Error during forceRefreshDevices:', error)\n return this.availableDevices\n } finally {\n this.refreshInProgress = false\n this.logger?.debug('Force refresh finished.')\n }\n }\n\n /**\n * Refresh the list of available devices with debouncing and notify listeners.\n * @returns Promise resolving to the updated device list (AudioDevice[])\n */\n async refreshDevices(): Promise<AudioDevice[]> {\n const now = Date.now()\n\n if (this.refreshInProgress) {\n this.logger?.debug('Refresh already in progress, skipping')\n return this.availableDevices\n }\n\n // Always allow refresh if forced by native event or longer than 2s debounce\n const timeSinceLastRefresh = now - this.lastRefreshTime\n const shouldDebounce =\n timeSinceLastRefresh < this.refreshDebounceMs &&\n timeSinceLastRefresh < 2000\n\n if (shouldDebounce) {\n this.logger?.debug(\n `Refresh debounced, skipping (last refresh was ${timeSinceLastRefresh}ms ago)`\n )\n return this.availableDevices\n }\n\n this.logger?.debug('Refreshing devices...')\n this.refreshInProgress = true\n try {\n // Fetch the latest devices; getAvailableDevices handles mapping now\n const devices = await this.getAvailableDevices({ refresh: true })\n // availableDevices state is updated within getAvailableDevices\n this.notifyListeners() // Notify listeners with the updated list\n this.lastRefreshTime = Date.now()\n return devices // Return the fetched & mapped list\n } catch (error) {\n this.logger?.error('Error during refreshDevices:', error)\n return this.availableDevices // Return potentially stale list on error\n } finally {\n this.refreshInProgress = false\n this.logger?.debug('Refresh finished.')\n }\n }\n\n /**\n * Get audio input devices using the Web Audio API\n * @returns Promise resolving to an array of AudioDevice objects\n */\n private async getWebAudioDevices(): Promise<AudioDevice[]> {\n if (\n typeof navigator === 'undefined' ||\n !navigator.mediaDevices ||\n !navigator.mediaDevices.enumerateDevices\n ) {\n return [DEFAULT_DEVICE]\n }\n\n try {\n const permissionStatus = await this.checkMicrophonePermission()\n\n if (permissionStatus === 'denied') {\n return [\n {\n ...DEFAULT_DEVICE,\n name: 'Microphone Access Denied',\n isAvailable: false,\n },\n ]\n }\n\n if (permissionStatus !== 'granted') {\n try {\n // Requesting stream often reveals device labels\n await navigator.mediaDevices.getUserMedia({ audio: true })\n } catch (error) {\n this.logger?.warn(\n 'Microphone permission request failed:',\n error\n )\n return [\n {\n ...DEFAULT_DEVICE,\n name: 'Microphone Access Required',\n isAvailable: false,\n },\n ]\n }\n }\n\n const devices = await navigator.mediaDevices.enumerateDevices()\n const audioInputDevices = devices\n .filter((device) => device.kind === 'audioinput')\n .map((device) => this.mapWebDeviceToAudioDevice(device))\n\n const hasUnlabeledDevices = audioInputDevices.some(\n (device) =>\n !device.name || device.name.startsWith('Microphone ')\n )\n\n let finalDevices = audioInputDevices\n if (hasUnlabeledDevices && this.isSafariOrIOS()) {\n finalDevices = this.enhanceDevicesForSafari(audioInputDevices)\n }\n\n if (finalDevices.length === 0) {\n finalDevices = [DEFAULT_DEVICE]\n }\n\n this.availableDevices = finalDevices // Update internal state\n return finalDevices\n } catch (error) {\n this.logger?.error('Failed to enumerate web audio devices:', error)\n this.availableDevices = [DEFAULT_DEVICE] // Update state on error\n return this.availableDevices\n }\n }\n\n /**\n * Check the current microphone permission status\n * @returns Permission state ('prompt', 'granted', or 'denied')\n */\n private async checkMicrophonePermission(): Promise<PermissionState> {\n if (!navigator.permissions || !navigator.permissions.query) {\n return 'prompt'\n }\n try {\n const permissionStatus = await navigator.permissions.query({\n name: 'microphone' as PermissionName,\n })\n permissionStatus.onchange = () => {\n // Refresh devices when permission changes\n this.refreshDevices()\n }\n return permissionStatus.state\n } catch (error) {\n this.logger?.warn('Permission query not supported:', error)\n return 'prompt'\n }\n }\n\n /**\n * Setup listener for device changes in web environment\n */\n private setupWebDeviceChangeListener(): void {\n if (\n typeof navigator === 'undefined' ||\n !navigator.mediaDevices ||\n this.webDeviceChangeHandler // Avoid adding multiple listeners\n ) {\n this.logger?.debug(\n 'Web device change listener not available or already set up'\n )\n return\n }\n\n try {\n this.webDeviceChangeHandler = () => {\n this.logger?.debug(\n 'Web device change detected, refreshing device list'\n )\n // Force refresh to get immediate updates\n this.forceRefreshDevices()\n }\n\n navigator.mediaDevices.addEventListener(\n 'devicechange',\n this.webDeviceChangeHandler\n )\n this.logger?.debug('Web device change listener successfully set up')\n } catch (error) {\n this.logger?.warn(\n 'Failed to set up web device change listener:',\n error\n )\n this.webDeviceChangeHandler = undefined\n }\n }\n\n /**\n * Check if the current browser is Safari or iOS WebKit\n */\n private isSafariOrIOS(): boolean {\n if (typeof navigator === 'undefined') return false\n const ua = navigator.userAgent\n return (\n /^((?!chrome|android).)*safari/i.test(ua) ||\n /iPad|iPhone|iPod/.test(ua) ||\n (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)\n )\n }\n\n /**\n * Create enhanced device information for Safari and privacy-restricted browsers\n * @param devices Array of AudioDevice objects, potentially unlabeled\n * @returns Array of enhanced AudioDevice objects\n */\n private enhanceDevicesForSafari(devices: AudioDevice[]): AudioDevice[] {\n const defaultDevice = devices.find((d) => d.isDefault)\n\n if (devices.length <= 1) {\n // Return a typed default device\n return [\n {\n id: defaultDevice?.id || 'default',\n name: 'Microphone (Browser Managed)',\n type: 'builtin_mic',\n isDefault: true,\n isAvailable: true,\n capabilities:\n defaultDevice?.capabilities ||\n DEFAULT_DEVICE.capabilities,\n },\n ]\n }\n\n // Provide more descriptive names for unlabeled devices\n return devices.map((device, index) => {\n if (!device.name || device.name.startsWith('Microphone ')) {\n const deviceTypes = [\n 'Built-in Microphone',\n 'External Microphone',\n 'Headset Microphone',\n ]\n const typeName = deviceTypes[index % deviceTypes.length]\n return {\n ...device,\n name: device.isDefault ? `${typeName} (Default)` : typeName,\n }\n }\n return device\n })\n }\n\n /**\n * Map a Web MediaDeviceInfo to our AudioDevice format\n * @param device The MediaDeviceInfo object from the browser\n * @returns An object conforming to the AudioDevice interface\n */\n private mapWebDeviceToAudioDevice(device: MediaDeviceInfo): AudioDevice {\n const isDefault = device.deviceId === 'default'\n const deviceType = this.inferDeviceType(device.label || '')\n\n // Provide reasonable default capabilities for web devices\n const defaultWebCapabilities: AudioDeviceCapabilities = {\n sampleRates: [16000, 44100, 48000],\n channelCounts: [1, 2],\n bitDepths: [16, 32], // Web Audio uses float32, common PCM might be 16/32\n hasEchoCancellation: true, // Often handled by browser\n hasNoiseSuppression: true, // Often handled by browser\n hasAutomaticGainControl: true, // Often handled by browser\n }\n\n return {\n id: device.deviceId,\n name:\n device.label || `Microphone ${device.deviceId.substring(0, 8)}`,\n type: deviceType,\n isDefault,\n isAvailable: true, // Assume available if enumerated\n capabilities: defaultWebCapabilities,\n }\n }\n\n /**\n * Try to infer the device type from its name\n * @param deviceName The label of the device\n * @returns A string representing the inferred device type\n */\n private inferDeviceType(deviceName: string): string {\n const name = deviceName.toLowerCase()\n if (name.includes('bluetooth') || name.includes('airpods'))\n return 'bluetooth'\n if (name.includes('usb')) return 'usb'\n if (name.includes('headphone') || name.includes('headset')) {\n return name.includes('wired') ? 'wired_headset' : 'wired_headphones'\n }\n if (name.includes('speaker')) return 'speaker'\n return 'builtin_mic' // Default assumption\n }\n\n /**\n * Notify all registered listeners about device changes.\n */\n notifyListeners(): void {\n // Pass a copy of the filtered devices array to listeners\n const devicesCopy = this.getFilteredDevices()\n\n this.logger?.debug(\n `Notifying ${this.deviceChangeListeners.size} listeners with ${devicesCopy.length} devices ` +\n `(${this.temporarilyDisconnectedDevices.size} temporarily hidden)`\n )\n\n this.deviceChangeListeners.forEach((listener) => {\n try {\n listener(devicesCopy)\n } catch (error) {\n this.logger?.error('Error in device change listener:', error)\n }\n })\n }\n}\n\n// Create and export the singleton instance\nexport const audioDeviceManager = new AudioDeviceManager()\n\nexport { DeviceDisconnectionBehavior }\n"]}
1
+ {"version":3,"file":"AudioDeviceManager.js","sourceRoot":"","sources":["../../src/AudioDeviceManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventEmitter } from 'expo-modules-core'\nimport { Platform } from 'react-native'\n\nimport {\n AudioDevice,\n AudioDeviceCapabilities,\n DeviceDisconnectionBehavior,\n ConsoleLike,\n} from './ExpoAudioStream.types'\nimport ExpoAudioStreamModule from './ExpoAudioStreamModule'\n\n// Default device fallback for web and unsupported platforms\nconst DEFAULT_DEVICE: AudioDevice = {\n id: 'default',\n name: 'Default Microphone',\n type: 'builtin_mic',\n isDefault: true,\n isAvailable: true,\n capabilities: {\n sampleRates: [16000, 44100, 48000],\n channelCounts: [1, 2],\n bitDepths: [16, 24, 32],\n hasEchoCancellation: true,\n hasNoiseSuppression: true,\n hasAutomaticGainControl: true,\n },\n}\n\n// Helper function to map raw object to AudioDevice interface\n// This handles potential inconsistencies from the native module\nfunction mapRawDeviceToAudioDevice(rawDevice: any): AudioDevice {\n const capabilities = rawDevice.capabilities || {}\n return {\n id: rawDevice.id || 'unknown',\n name: rawDevice.name || 'Unknown Device',\n type: rawDevice.type || 'unknown',\n isDefault: rawDevice.isDefault || false,\n isAvailable:\n rawDevice.isAvailable !== undefined ? rawDevice.isAvailable : true, // Default to true if undefined\n capabilities: {\n sampleRates: capabilities.sampleRates || [16000, 44100, 48000], // Provide defaults\n channelCounts: capabilities.channelCounts || [1, 2],\n bitDepths: capabilities.bitDepths || [16, 24, 32],\n hasEchoCancellation: capabilities.hasEchoCancellation,\n hasNoiseSuppression: capabilities.hasNoiseSuppression,\n hasAutomaticGainControl: capabilities.hasAutomaticGainControl,\n },\n }\n}\n\n/**\n * Class that provides a cross-platform API for managing audio input devices\n *\n * EVENT API SPECIFICATION:\n * ========================\n *\n * Device Events (deviceChangedEvent):\n * ```\n * {\n * type: \"deviceConnected\" | \"deviceDisconnected\",\n * deviceId: string\n * }\n * ```\n *\n * Recording Interruption Events (recordingInterruptedEvent):\n * ```\n * {\n * reason: \"userPaused\" | \"userResumed\" | \"audioFocusLoss\" | \"audioFocusGain\" |\n * \"deviceFallback\" | \"deviceSwitchFailed\" | \"phoneCall\" | \"phoneCallEnded\",\n * isPaused: boolean,\n * timestamp: number\n * }\n * ```\n *\n * NOTE: Device events use \"type\" field, interruption events use \"reason\" field.\n * This is intentional to distinguish between different event categories.\n */\nexport class AudioDeviceManager {\n private eventEmitter: InstanceType<typeof EventEmitter>\n private currentDeviceId: string | null = null\n private availableDevices: AudioDevice[] = []\n private deviceChangeListeners: Set<(devices: AudioDevice[]) => void> =\n new Set()\n private webDeviceChangeHandler?: () => void\n private lastRefreshTime: number = 0\n private refreshInProgress: boolean = false\n private refreshDebounceMs: number = 500 // Minimum 500ms between refreshes\n private logger?: ConsoleLike\n\n // Track temporarily disconnected devices\n private temporarilyDisconnectedDevices: Set<string> = new Set()\n private disconnectionTimeouts: Map<string, NodeJS.Timeout> = new Map()\n private readonly DISCONNECTION_TIMEOUT_MS = 5000 // 5 seconds\n\n constructor(options?: { logger?: ConsoleLike }) {\n this.eventEmitter = new EventEmitter(ExpoAudioStreamModule)\n this.logger = options?.logger\n\n // Set up device event listeners for all platforms immediately\n this.setupDeviceEventListeners()\n }\n\n /**\n * Set up device event listeners for the current platform\n */\n private setupDeviceEventListeners(): void {\n if (Platform.OS === 'web') {\n this.setupWebDeviceChangeListener()\n } else {\n this.setupNativeDeviceEventListener()\n }\n }\n\n /**\n * Set up native device event listener for iOS/Android\n */\n private setupNativeDeviceEventListener(): void {\n // Store the last event type to avoid duplicates\n let lastEventType: string | null = null\n let lastEventTime = 0\n\n this.eventEmitter.addListener('deviceChangedEvent', (event: any) => {\n // Skip processing duplicate events that occur too close together\n const now = Date.now()\n const isSimilarEvent =\n lastEventType === event.type &&\n now - lastEventTime < this.refreshDebounceMs\n\n if (isSimilarEvent) {\n this.logger?.debug(\n `Skipping similar device event (${event.type}) received too soon`\n )\n return\n }\n\n // Update the last event tracking\n lastEventType = event.type\n lastEventTime = now\n\n // Only refresh on meaningful events\n if (\n event.type === 'deviceConnected' ||\n event.type === 'deviceDisconnected' ||\n event.type === 'routeChanged'\n ) {\n this.logger?.debug(`Processing device event: ${event.type}`)\n // Force refresh for device events to ensure fresh data\n this.forceRefreshDevices()\n }\n })\n this.logger?.debug('Native device event listener set up')\n }\n\n /**\n * Initialize the device manager with a logger\n * @param logger A logger instance that implements the ConsoleLike interface\n * @returns The manager instance for chaining\n */\n initWithLogger(logger: ConsoleLike): AudioDeviceManager {\n this.setLogger(logger)\n return this\n }\n\n /**\n * Set the logger instance\n * @param logger A logger instance that implements the ConsoleLike interface\n */\n setLogger(logger: ConsoleLike) {\n this.logger = logger\n }\n\n /**\n * Initialize or reinitialize device detection\n * Useful for restarting device detection if initial setup failed\n */\n initializeDeviceDetection(): void {\n this.logger?.debug('Initializing device detection...')\n\n // Clean up existing listeners first\n if (Platform.OS === 'web' && this.webDeviceChangeHandler) {\n if (typeof navigator !== 'undefined' && navigator.mediaDevices) {\n navigator.mediaDevices.removeEventListener(\n 'devicechange',\n this.webDeviceChangeHandler\n )\n }\n this.webDeviceChangeHandler = undefined\n }\n\n // Re-setup device event listeners\n this.setupDeviceEventListeners()\n }\n\n /**\n * Get the current logger instance\n * @returns The logger instance or undefined if not set\n */\n getLogger(): ConsoleLike | undefined {\n return this.logger\n }\n\n /**\n * Get all available audio input devices\n * @param options Optional settings to force refresh the device list. Can include a refresh flag.\n * @returns Promise resolving to an array of audio devices conforming to AudioDevice interface\n */\n async getAvailableDevices(options?: {\n refresh?: boolean\n }): Promise<AudioDevice[]> {\n try {\n if (Platform.OS === 'web') {\n this.availableDevices = await this.getWebAudioDevices()\n } else if (ExpoAudioStreamModule.getAvailableInputDevices) {\n // Expecting an array of raw device objects from native\n const rawDevices: any[] =\n await ExpoAudioStreamModule.getAvailableInputDevices(\n options\n )\n // Map raw objects to the AudioDevice interface\n this.availableDevices = rawDevices.map(\n mapRawDeviceToAudioDevice\n )\n } else {\n // Fallback for unsupported platforms\n this.availableDevices = [DEFAULT_DEVICE]\n }\n return this.availableDevices\n } catch (error) {\n this.logger?.error('Failed to get available devices:', error)\n this.availableDevices = [DEFAULT_DEVICE] // Ensure state is updated on error\n return this.availableDevices\n }\n }\n\n /**\n * Get the currently selected audio input device\n * @returns Promise resolving to the current device (conforming to AudioDevice) or null\n */\n async getCurrentDevice(): Promise<AudioDevice | null> {\n try {\n if (Platform.OS === 'web') {\n if (!this.currentDeviceId) {\n // On web, return the typed default device if nothing is selected\n return DEFAULT_DEVICE\n }\n // Refresh web devices to ensure the current one is up-to-date\n const webDevices = await this.getWebAudioDevices()\n return (\n webDevices.find((d) => d.id === this.currentDeviceId) ||\n DEFAULT_DEVICE // Fallback to default if current ID not found\n )\n } else if (ExpoAudioStreamModule.getCurrentInputDevice) {\n // Expecting a single raw device object or null from native\n const rawDevice: any | null =\n await ExpoAudioStreamModule.getCurrentInputDevice()\n // Map to AudioDevice interface if not null\n return rawDevice ? mapRawDeviceToAudioDevice(rawDevice) : null\n } else {\n // Fallback for unsupported platforms\n return DEFAULT_DEVICE\n }\n } catch (error) {\n this.logger?.error('Failed to get current device:', error)\n return DEFAULT_DEVICE // Return default on error\n }\n }\n\n /**\n * Select a specific audio input device for recording\n * @param deviceId The ID of the device to select\n * @returns Promise resolving to a boolean indicating success\n */\n async selectDevice(deviceId: string): Promise<boolean> {\n try {\n let success = false\n if (Platform.OS === 'web') {\n // Check if the device exists before setting it\n const devices = await this.getWebAudioDevices()\n if (devices.some((d) => d.id === deviceId)) {\n this.currentDeviceId = deviceId\n success = true\n } else {\n this.logger?.warn(\n `Web: Device with ID ${deviceId} not found.`\n )\n success = false\n }\n } else if (ExpoAudioStreamModule.selectInputDevice) {\n success =\n await ExpoAudioStreamModule.selectInputDevice(deviceId)\n if (success) {\n this.currentDeviceId = deviceId\n }\n }\n // Refresh devices after selection attempt to update state\n await this.refreshDevices()\n return success\n } catch (error) {\n this.logger?.error('Failed to select device:', error)\n await this.refreshDevices() // Refresh even on error\n return false\n }\n }\n\n /**\n * Reset to the default audio input device\n * @returns Promise resolving to a boolean indicating success\n */\n async resetToDefaultDevice(): Promise<boolean> {\n try {\n let success = false\n if (Platform.OS === 'web') {\n this.currentDeviceId = 'default'\n success = true\n } else if (ExpoAudioStreamModule.resetToDefaultDevice) {\n success = await ExpoAudioStreamModule.resetToDefaultDevice()\n if (success) {\n this.currentDeviceId = null\n }\n }\n // Refresh devices after reset attempt\n await this.refreshDevices()\n return success\n } catch (error) {\n this.logger?.error('Failed to reset to default device:', error)\n await this.refreshDevices() // Refresh even on error\n return false\n }\n }\n\n /**\n * Register a listener for device changes\n * @param listener Function to call when devices change (receives AudioDevice[])\n * @returns Function to remove the listener\n */\n addDeviceChangeListener(\n listener: (devices: AudioDevice[]) => void\n ): () => void {\n this.deviceChangeListeners.add(listener)\n\n // Immediately call listener with current devices if available\n if (this.availableDevices.length > 0) {\n listener([...this.availableDevices])\n }\n\n // Return a function to remove the listener\n return () => {\n this.deviceChangeListeners.delete(listener)\n }\n }\n\n /**\n * Mark a device as temporarily disconnected (for UI filtering)\n * @param deviceId The ID of the device that was disconnected\n * @param notify Whether to notify listeners immediately (default: true)\n */\n markDeviceAsDisconnected(deviceId: string, notify: boolean = true): void {\n this.logger?.debug(\n `Marking device ${deviceId} as temporarily disconnected`\n )\n\n // Clear any existing timeout for this device\n const existingTimeout = this.disconnectionTimeouts.get(deviceId)\n if (existingTimeout) {\n clearTimeout(existingTimeout)\n }\n\n // Add to disconnected set\n this.temporarilyDisconnectedDevices.add(deviceId)\n\n // Set timeout to remove from disconnected set\n const timeout = setTimeout(() => {\n this.logger?.debug(\n `Reconnection timeout expired for device ${deviceId}`\n )\n this.temporarilyDisconnectedDevices.delete(deviceId)\n this.disconnectionTimeouts.delete(deviceId)\n // Refresh devices to show the device again if it's still available\n this.forceRefreshDevices()\n }, this.DISCONNECTION_TIMEOUT_MS)\n\n this.disconnectionTimeouts.set(deviceId, timeout)\n\n // Only notify listeners if requested\n if (notify) {\n this.notifyListeners()\n }\n }\n\n /**\n * Mark a device as reconnected (remove from disconnected set)\n * @param deviceId The ID of the device that was reconnected\n */\n markDeviceAsReconnected(deviceId: string): void {\n this.logger?.debug(`Marking device ${deviceId} as reconnected`)\n\n // Clear timeout and remove from disconnected set\n const timeout = this.disconnectionTimeouts.get(deviceId)\n if (timeout) {\n clearTimeout(timeout)\n this.disconnectionTimeouts.delete(deviceId)\n }\n\n this.temporarilyDisconnectedDevices.delete(deviceId)\n\n // Notify listeners with updated device list\n this.notifyListeners()\n }\n\n /**\n * Get filtered device list (excluding temporarily disconnected devices)\n * @returns Array of available devices excluding temporarily disconnected ones\n */\n private getFilteredDevices(): AudioDevice[] {\n if (this.temporarilyDisconnectedDevices.size === 0) {\n return [...this.availableDevices]\n }\n\n const filtered = this.availableDevices.filter(\n (device) => !this.temporarilyDisconnectedDevices.has(device.id)\n )\n\n this.logger?.debug(\n `Filtered ${this.availableDevices.length - filtered.length} temporarily disconnected devices. ` +\n `Showing ${filtered.length} devices.`\n )\n\n return filtered\n }\n\n /**\n * Get the raw device list (including temporarily disconnected devices)\n * @returns Array of all available devices from native layer\n */\n getRawDevices(): AudioDevice[] {\n return [...this.availableDevices]\n }\n\n /**\n * Get the IDs of temporarily disconnected devices\n * @returns Set of device IDs that are temporarily hidden from UI\n */\n getTemporarilyDisconnectedDeviceIds(): ReadonlySet<string> {\n return new Set(this.temporarilyDisconnectedDevices)\n }\n\n /**\n * Clean up timeouts and listeners (useful for testing or cleanup)\n */\n cleanup(): void {\n // Clear all disconnection timeouts\n this.disconnectionTimeouts.forEach((timeout) => clearTimeout(timeout))\n this.disconnectionTimeouts.clear()\n this.temporarilyDisconnectedDevices.clear()\n\n // Clear device change listeners\n this.deviceChangeListeners.clear()\n\n // Clean up web device listener\n if (Platform.OS === 'web' && this.webDeviceChangeHandler) {\n if (typeof navigator !== 'undefined' && navigator.mediaDevices) {\n navigator.mediaDevices.removeEventListener(\n 'devicechange',\n this.webDeviceChangeHandler\n )\n }\n this.webDeviceChangeHandler = undefined\n }\n\n this.logger?.debug('AudioDeviceManager cleanup completed')\n }\n\n /**\n * Force refresh devices without debouncing (for device events)\n * @returns Promise resolving to the updated device list (AudioDevice[])\n */\n async forceRefreshDevices(): Promise<AudioDevice[]> {\n this.logger?.debug('Force refreshing devices (bypassing debounce)...')\n this.refreshInProgress = true\n try {\n // Force fetch the latest devices from native layer\n const devices = await this.getAvailableDevices({ refresh: true })\n // Update internal state\n this.availableDevices = devices\n // Notify listeners with fresh data\n this.notifyListeners()\n this.lastRefreshTime = Date.now()\n return devices\n } catch (error) {\n this.logger?.error('Error during forceRefreshDevices:', error)\n return this.availableDevices\n } finally {\n this.refreshInProgress = false\n this.logger?.debug('Force refresh finished.')\n }\n }\n\n /**\n * Refresh the list of available devices with debouncing and notify listeners.\n * @returns Promise resolving to the updated device list (AudioDevice[])\n */\n async refreshDevices(): Promise<AudioDevice[]> {\n const now = Date.now()\n\n if (this.refreshInProgress) {\n this.logger?.debug('Refresh already in progress, skipping')\n return this.availableDevices\n }\n\n // Always allow refresh if forced by native event or longer than 2s debounce\n const timeSinceLastRefresh = now - this.lastRefreshTime\n const shouldDebounce =\n timeSinceLastRefresh < this.refreshDebounceMs &&\n timeSinceLastRefresh < 2000\n\n if (shouldDebounce) {\n this.logger?.debug(\n `Refresh debounced, skipping (last refresh was ${timeSinceLastRefresh}ms ago)`\n )\n return this.availableDevices\n }\n\n this.logger?.debug('Refreshing devices...')\n this.refreshInProgress = true\n try {\n // Fetch the latest devices; getAvailableDevices handles mapping now\n const devices = await this.getAvailableDevices({ refresh: true })\n // availableDevices state is updated within getAvailableDevices\n this.notifyListeners() // Notify listeners with the updated list\n this.lastRefreshTime = Date.now()\n return devices // Return the fetched & mapped list\n } catch (error) {\n this.logger?.error('Error during refreshDevices:', error)\n return this.availableDevices // Return potentially stale list on error\n } finally {\n this.refreshInProgress = false\n this.logger?.debug('Refresh finished.')\n }\n }\n\n /**\n * Get audio input devices using the Web Audio API\n * @returns Promise resolving to an array of AudioDevice objects\n */\n private async getWebAudioDevices(): Promise<AudioDevice[]> {\n if (\n typeof navigator === 'undefined' ||\n !navigator.mediaDevices ||\n !navigator.mediaDevices.enumerateDevices\n ) {\n return [DEFAULT_DEVICE]\n }\n\n try {\n const permissionStatus = await this.checkMicrophonePermission()\n\n if (permissionStatus === 'denied') {\n return [\n {\n ...DEFAULT_DEVICE,\n name: 'Microphone Access Denied',\n isAvailable: false,\n },\n ]\n }\n\n if (permissionStatus !== 'granted') {\n try {\n // Requesting stream often reveals device labels\n await navigator.mediaDevices.getUserMedia({ audio: true })\n } catch (error) {\n this.logger?.warn(\n 'Microphone permission request failed:',\n error\n )\n return [\n {\n ...DEFAULT_DEVICE,\n name: 'Microphone Access Required',\n isAvailable: false,\n },\n ]\n }\n }\n\n const devices = await navigator.mediaDevices.enumerateDevices()\n const audioInputDevices = devices\n .filter((device) => device.kind === 'audioinput')\n .map((device) => this.mapWebDeviceToAudioDevice(device))\n\n const hasUnlabeledDevices = audioInputDevices.some(\n (device) =>\n !device.name || device.name.startsWith('Microphone ')\n )\n\n let finalDevices = audioInputDevices\n if (hasUnlabeledDevices && this.isSafariOrIOS()) {\n finalDevices = this.enhanceDevicesForSafari(audioInputDevices)\n }\n\n if (finalDevices.length === 0) {\n finalDevices = [DEFAULT_DEVICE]\n }\n\n this.availableDevices = finalDevices // Update internal state\n return finalDevices\n } catch (error) {\n this.logger?.error('Failed to enumerate web audio devices:', error)\n this.availableDevices = [DEFAULT_DEVICE] // Update state on error\n return this.availableDevices\n }\n }\n\n /**\n * Check the current microphone permission status\n * @returns Permission state ('prompt', 'granted', or 'denied')\n */\n private async checkMicrophonePermission(): Promise<PermissionState> {\n if (!navigator.permissions || !navigator.permissions.query) {\n return 'prompt'\n }\n try {\n const permissionStatus = await navigator.permissions.query({\n name: 'microphone' as PermissionName,\n })\n permissionStatus.onchange = () => {\n // Refresh devices when permission changes\n this.refreshDevices()\n }\n return permissionStatus.state\n } catch (error) {\n this.logger?.warn('Permission query not supported:', error)\n return 'prompt'\n }\n }\n\n /**\n * Setup listener for device changes in web environment\n */\n private setupWebDeviceChangeListener(): void {\n if (\n typeof navigator === 'undefined' ||\n !navigator.mediaDevices ||\n this.webDeviceChangeHandler // Avoid adding multiple listeners\n ) {\n this.logger?.debug(\n 'Web device change listener not available or already set up'\n )\n return\n }\n\n try {\n this.webDeviceChangeHandler = () => {\n this.logger?.debug(\n 'Web device change detected, refreshing device list'\n )\n // Force refresh to get immediate updates\n this.forceRefreshDevices()\n }\n\n navigator.mediaDevices.addEventListener(\n 'devicechange',\n this.webDeviceChangeHandler\n )\n this.logger?.debug('Web device change listener successfully set up')\n } catch (error) {\n this.logger?.warn(\n 'Failed to set up web device change listener:',\n error\n )\n this.webDeviceChangeHandler = undefined\n }\n }\n\n /**\n * Check if the current browser is Safari or iOS WebKit\n */\n private isSafariOrIOS(): boolean {\n if (typeof navigator === 'undefined') return false\n const ua = navigator.userAgent\n return (\n /^((?!chrome|android).)*safari/i.test(ua) ||\n /iPad|iPhone|iPod/.test(ua) ||\n (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)\n )\n }\n\n /**\n * Create enhanced device information for Safari and privacy-restricted browsers\n * @param devices Array of AudioDevice objects, potentially unlabeled\n * @returns Array of enhanced AudioDevice objects\n */\n private enhanceDevicesForSafari(devices: AudioDevice[]): AudioDevice[] {\n const defaultDevice = devices.find((d) => d.isDefault)\n\n if (devices.length <= 1) {\n // Return a typed default device\n return [\n {\n id: defaultDevice?.id || 'default',\n name: 'Microphone (Browser Managed)',\n type: 'builtin_mic',\n isDefault: true,\n isAvailable: true,\n capabilities:\n defaultDevice?.capabilities ||\n DEFAULT_DEVICE.capabilities,\n },\n ]\n }\n\n // Provide more descriptive names for unlabeled devices\n return devices.map((device, index) => {\n if (!device.name || device.name.startsWith('Microphone ')) {\n const deviceTypes = [\n 'Built-in Microphone',\n 'External Microphone',\n 'Headset Microphone',\n ]\n const typeName = deviceTypes[index % deviceTypes.length]\n return {\n ...device,\n name: device.isDefault ? `${typeName} (Default)` : typeName,\n }\n }\n return device\n })\n }\n\n /**\n * Map a Web MediaDeviceInfo to our AudioDevice format\n * @param device The MediaDeviceInfo object from the browser\n * @returns An object conforming to the AudioDevice interface\n */\n private mapWebDeviceToAudioDevice(device: MediaDeviceInfo): AudioDevice {\n const isDefault = device.deviceId === 'default'\n const deviceType = this.inferDeviceType(device.label || '')\n\n // Provide reasonable default capabilities for web devices\n const defaultWebCapabilities: AudioDeviceCapabilities = {\n sampleRates: [16000, 44100, 48000],\n channelCounts: [1, 2],\n bitDepths: [16, 32], // Web Audio uses float32, common PCM might be 16/32\n hasEchoCancellation: true, // Often handled by browser\n hasNoiseSuppression: true, // Often handled by browser\n hasAutomaticGainControl: true, // Often handled by browser\n }\n\n return {\n id: device.deviceId,\n name:\n device.label || `Microphone ${device.deviceId.substring(0, 8)}`,\n type: deviceType,\n isDefault,\n isAvailable: true, // Assume available if enumerated\n capabilities: defaultWebCapabilities,\n }\n }\n\n /**\n * Try to infer the device type from its name\n * @param deviceName The label of the device\n * @returns A string representing the inferred device type\n */\n private inferDeviceType(deviceName: string): string {\n const name = deviceName.toLowerCase()\n if (name.includes('bluetooth') || name.includes('airpods'))\n return 'bluetooth'\n if (name.includes('usb')) return 'usb'\n if (name.includes('headphone') || name.includes('headset')) {\n return name.includes('wired') ? 'wired_headset' : 'wired_headphones'\n }\n if (name.includes('speaker')) return 'speaker'\n return 'builtin_mic' // Default assumption\n }\n\n /**\n * Notify all registered listeners about device changes.\n */\n notifyListeners(): void {\n // Pass a copy of the filtered devices array to listeners\n const devicesCopy = this.getFilteredDevices()\n\n this.logger?.debug(\n `Notifying ${this.deviceChangeListeners.size} listeners with ${devicesCopy.length} devices ` +\n `(${this.temporarilyDisconnectedDevices.size} temporarily hidden)`\n )\n\n this.deviceChangeListeners.forEach((listener) => {\n try {\n listener(devicesCopy)\n } catch (error) {\n this.logger?.error('Error in device change listener:', error)\n }\n })\n }\n}\n\n// Create and export the singleton instance\nexport const audioDeviceManager = new AudioDeviceManager()\n\nexport { DeviceDisconnectionBehavior }\n"]}
@@ -92,6 +92,7 @@ class WebRecorder {
92
92
  numberOfChannels: this.numberOfChannels,
93
93
  sampleRate: this.config.sampleRate || this.audioContext.sampleRate,
94
94
  segmentDurationMs: this.config.segmentDurationMs ?? DEFAULT_SEGMENT_DURATION_MS, // Default to 100ms segments
95
+ extractionTimeMs: 0,
95
96
  };
96
97
  if (recordingConfig.enableProcessing) {
97
98
  this.initFeatureExtractorWorker();