@siteed/expo-audio-studio 2.7.0 → 2.8.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 (241) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/app.plugin.cjs +15 -2
  3. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js +4 -0
  4. package/build/cjs/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
  5. package/build/cjs/AudioAnalysis/extractAudioAnalysis.js +205 -0
  6. package/build/cjs/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
  7. package/build/cjs/AudioAnalysis/extractAudioData.js +12 -0
  8. package/build/cjs/AudioAnalysis/extractAudioData.js.map +1 -0
  9. package/build/cjs/AudioAnalysis/extractMelSpectrogram.js +89 -0
  10. package/build/cjs/AudioAnalysis/extractMelSpectrogram.js.map +1 -0
  11. package/build/cjs/AudioAnalysis/extractPreview.js +29 -0
  12. package/build/cjs/AudioAnalysis/extractPreview.js.map +1 -0
  13. package/build/cjs/AudioAnalysis/extractWaveform.js +18 -0
  14. package/build/cjs/AudioAnalysis/extractWaveform.js.map +1 -0
  15. package/build/cjs/AudioDeviceManager.js +500 -0
  16. package/build/cjs/AudioDeviceManager.js.map +1 -0
  17. package/build/cjs/AudioRecorder.provider.js +68 -0
  18. package/build/cjs/AudioRecorder.provider.js.map +1 -0
  19. package/build/cjs/ExpoAudioStream.native.js +8 -0
  20. package/build/cjs/ExpoAudioStream.native.js.map +1 -0
  21. package/build/cjs/ExpoAudioStream.types.js +11 -0
  22. package/build/cjs/ExpoAudioStream.types.js.map +1 -0
  23. package/build/cjs/ExpoAudioStream.web.js +705 -0
  24. package/build/cjs/ExpoAudioStream.web.js.map +1 -0
  25. package/build/cjs/ExpoAudioStreamModule.js +718 -0
  26. package/build/cjs/ExpoAudioStreamModule.js.map +1 -0
  27. package/build/cjs/WebRecorder.web.js +756 -0
  28. package/build/cjs/WebRecorder.web.js.map +1 -0
  29. package/build/cjs/constants.js +17 -0
  30. package/build/cjs/constants.js.map +1 -0
  31. package/build/cjs/events.js +30 -0
  32. package/build/cjs/events.js.map +1 -0
  33. package/build/cjs/hooks/useAudioDevices.js +155 -0
  34. package/build/cjs/hooks/useAudioDevices.js.map +1 -0
  35. package/build/cjs/index.js +50 -0
  36. package/build/cjs/index.js.map +1 -0
  37. package/build/cjs/trimAudio.js +75 -0
  38. package/build/cjs/trimAudio.js.map +1 -0
  39. package/build/cjs/useAudioRecorder.js +453 -0
  40. package/build/cjs/useAudioRecorder.js.map +1 -0
  41. package/build/cjs/utils/BlobFix.js +502 -0
  42. package/build/cjs/utils/BlobFix.js.map +1 -0
  43. package/build/cjs/utils/audioProcessing.js +137 -0
  44. package/build/cjs/utils/audioProcessing.js.map +1 -0
  45. package/build/cjs/utils/concatenateBuffers.js +25 -0
  46. package/build/cjs/utils/concatenateBuffers.js.map +1 -0
  47. package/build/cjs/utils/convertPCMToFloat32.js +124 -0
  48. package/build/cjs/utils/convertPCMToFloat32.js.map +1 -0
  49. package/build/cjs/utils/crc32.js +52 -0
  50. package/build/cjs/utils/crc32.js.map +1 -0
  51. package/build/cjs/utils/encodingToBitDepth.js +17 -0
  52. package/build/cjs/utils/encodingToBitDepth.js.map +1 -0
  53. package/build/cjs/utils/getWavFileInfo.js +96 -0
  54. package/build/cjs/utils/getWavFileInfo.js.map +1 -0
  55. package/build/cjs/utils/writeWavHeader.js +88 -0
  56. package/build/cjs/utils/writeWavHeader.js.map +1 -0
  57. package/build/cjs/workers/InlineFeaturesExtractor.web.js +853 -0
  58. package/build/cjs/workers/InlineFeaturesExtractor.web.js.map +1 -0
  59. package/build/cjs/workers/inlineAudioWebWorker.web.js +184 -0
  60. package/build/cjs/workers/inlineAudioWebWorker.web.js.map +1 -0
  61. package/build/esm/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
  62. package/build/esm/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
  63. package/build/esm/AudioAnalysis/extractAudioData.js.map +1 -0
  64. package/build/esm/AudioAnalysis/extractMelSpectrogram.js.map +1 -0
  65. package/build/esm/AudioAnalysis/extractPreview.js.map +1 -0
  66. package/build/esm/AudioAnalysis/extractWaveform.js.map +1 -0
  67. package/build/esm/AudioDeviceManager.js.map +1 -0
  68. package/build/esm/AudioRecorder.provider.js.map +1 -0
  69. package/build/esm/ExpoAudioStream.native.js.map +1 -0
  70. package/build/esm/ExpoAudioStream.types.js.map +1 -0
  71. package/build/esm/ExpoAudioStream.web.js.map +1 -0
  72. package/build/esm/ExpoAudioStreamModule.js.map +1 -0
  73. package/build/esm/WebRecorder.web.js.map +1 -0
  74. package/build/esm/constants.js.map +1 -0
  75. package/build/esm/events.js.map +1 -0
  76. package/build/esm/hooks/useAudioDevices.js.map +1 -0
  77. package/build/esm/index.js.map +1 -0
  78. package/build/esm/trimAudio.js.map +1 -0
  79. package/build/esm/useAudioRecorder.js.map +1 -0
  80. package/build/esm/utils/BlobFix.js.map +1 -0
  81. package/build/esm/utils/audioProcessing.js.map +1 -0
  82. package/build/esm/utils/concatenateBuffers.js.map +1 -0
  83. package/build/esm/utils/convertPCMToFloat32.js.map +1 -0
  84. package/build/esm/utils/crc32.js.map +1 -0
  85. package/build/esm/utils/encodingToBitDepth.js.map +1 -0
  86. package/build/esm/utils/getWavFileInfo.js.map +1 -0
  87. package/build/esm/utils/writeWavHeader.js.map +1 -0
  88. package/build/esm/workers/InlineFeaturesExtractor.web.js.map +1 -0
  89. package/build/esm/workers/inlineAudioWebWorker.web.js.map +1 -0
  90. package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -0
  91. package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -0
  92. package/build/types/AudioAnalysis/extractAudioData.d.ts.map +1 -0
  93. package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts.map +1 -0
  94. package/build/types/AudioAnalysis/extractPreview.d.ts.map +1 -0
  95. package/build/types/AudioAnalysis/extractWaveform.d.ts.map +1 -0
  96. package/build/types/AudioDeviceManager.d.ts.map +1 -0
  97. package/build/types/AudioRecorder.provider.d.ts.map +1 -0
  98. package/build/types/ExpoAudioStream.native.d.ts.map +1 -0
  99. package/build/types/ExpoAudioStream.types.d.ts.map +1 -0
  100. package/build/types/ExpoAudioStream.web.d.ts.map +1 -0
  101. package/build/types/ExpoAudioStreamModule.d.ts.map +1 -0
  102. package/build/types/WebRecorder.web.d.ts.map +1 -0
  103. package/build/types/constants.d.ts.map +1 -0
  104. package/build/types/events.d.ts.map +1 -0
  105. package/build/types/hooks/useAudioDevices.d.ts.map +1 -0
  106. package/build/types/index.d.ts.map +1 -0
  107. package/build/types/trimAudio.d.ts.map +1 -0
  108. package/build/types/useAudioRecorder.d.ts.map +1 -0
  109. package/build/types/utils/BlobFix.d.ts.map +1 -0
  110. package/build/types/utils/audioProcessing.d.ts.map +1 -0
  111. package/build/types/utils/concatenateBuffers.d.ts.map +1 -0
  112. package/build/types/utils/convertPCMToFloat32.d.ts.map +1 -0
  113. package/build/types/utils/crc32.d.ts.map +1 -0
  114. package/build/types/utils/encodingToBitDepth.d.ts.map +1 -0
  115. package/build/types/utils/getWavFileInfo.d.ts.map +1 -0
  116. package/build/types/utils/writeWavHeader.d.ts.map +1 -0
  117. package/build/types/workers/InlineFeaturesExtractor.web.d.ts.map +1 -0
  118. package/build/types/workers/inlineAudioWebWorker.web.d.ts.map +1 -0
  119. package/ios/AudioNotificationManager.swift +42 -19
  120. package/ios/AudioProcessingHelpers.swift +5 -5
  121. package/ios/AudioProcessor.swift +44 -218
  122. package/ios/AudioStreamManager.swift +121 -61
  123. package/ios/DataPoint.swift +5 -5
  124. package/ios/ExpoAudioStreamModule.swift +2 -1
  125. package/package.json +25 -7
  126. package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +0 -1
  127. package/build/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
  128. package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +0 -1
  129. package/build/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
  130. package/build/AudioAnalysis/extractAudioData.d.ts.map +0 -1
  131. package/build/AudioAnalysis/extractAudioData.js.map +0 -1
  132. package/build/AudioAnalysis/extractMelSpectrogram.d.ts.map +0 -1
  133. package/build/AudioAnalysis/extractMelSpectrogram.js.map +0 -1
  134. package/build/AudioAnalysis/extractPreview.d.ts.map +0 -1
  135. package/build/AudioAnalysis/extractPreview.js.map +0 -1
  136. package/build/AudioAnalysis/extractWaveform.d.ts.map +0 -1
  137. package/build/AudioAnalysis/extractWaveform.js.map +0 -1
  138. package/build/AudioDeviceManager.d.ts.map +0 -1
  139. package/build/AudioDeviceManager.js.map +0 -1
  140. package/build/AudioRecorder.provider.d.ts.map +0 -1
  141. package/build/AudioRecorder.provider.js.map +0 -1
  142. package/build/ExpoAudioStream.native.d.ts.map +0 -1
  143. package/build/ExpoAudioStream.native.js.map +0 -1
  144. package/build/ExpoAudioStream.types.d.ts.map +0 -1
  145. package/build/ExpoAudioStream.types.js.map +0 -1
  146. package/build/ExpoAudioStream.web.d.ts.map +0 -1
  147. package/build/ExpoAudioStream.web.js.map +0 -1
  148. package/build/ExpoAudioStreamModule.d.ts.map +0 -1
  149. package/build/ExpoAudioStreamModule.js.map +0 -1
  150. package/build/WebRecorder.web.d.ts.map +0 -1
  151. package/build/WebRecorder.web.js.map +0 -1
  152. package/build/constants.d.ts.map +0 -1
  153. package/build/constants.js.map +0 -1
  154. package/build/events.d.ts.map +0 -1
  155. package/build/events.js.map +0 -1
  156. package/build/hooks/useAudioDevices.d.ts.map +0 -1
  157. package/build/hooks/useAudioDevices.js.map +0 -1
  158. package/build/index.d.ts.map +0 -1
  159. package/build/index.js.map +0 -1
  160. package/build/trimAudio.d.ts.map +0 -1
  161. package/build/trimAudio.js.map +0 -1
  162. package/build/useAudioRecorder.d.ts.map +0 -1
  163. package/build/useAudioRecorder.js.map +0 -1
  164. package/build/utils/BlobFix.d.ts.map +0 -1
  165. package/build/utils/BlobFix.js.map +0 -1
  166. package/build/utils/audioProcessing.d.ts.map +0 -1
  167. package/build/utils/audioProcessing.js.map +0 -1
  168. package/build/utils/concatenateBuffers.d.ts.map +0 -1
  169. package/build/utils/concatenateBuffers.js.map +0 -1
  170. package/build/utils/convertPCMToFloat32.d.ts.map +0 -1
  171. package/build/utils/convertPCMToFloat32.js.map +0 -1
  172. package/build/utils/crc32.d.ts.map +0 -1
  173. package/build/utils/crc32.js.map +0 -1
  174. package/build/utils/encodingToBitDepth.d.ts.map +0 -1
  175. package/build/utils/encodingToBitDepth.js.map +0 -1
  176. package/build/utils/getWavFileInfo.d.ts.map +0 -1
  177. package/build/utils/getWavFileInfo.js.map +0 -1
  178. package/build/utils/writeWavHeader.d.ts.map +0 -1
  179. package/build/utils/writeWavHeader.js.map +0 -1
  180. package/build/workers/InlineFeaturesExtractor.web.d.ts.map +0 -1
  181. package/build/workers/InlineFeaturesExtractor.web.js.map +0 -1
  182. package/build/workers/inlineAudioWebWorker.web.d.ts.map +0 -1
  183. package/build/workers/inlineAudioWebWorker.web.js.map +0 -1
  184. /package/build/{AudioAnalysis → esm/AudioAnalysis}/AudioAnalysis.types.js +0 -0
  185. /package/build/{AudioAnalysis → esm/AudioAnalysis}/extractAudioAnalysis.js +0 -0
  186. /package/build/{AudioAnalysis → esm/AudioAnalysis}/extractAudioData.js +0 -0
  187. /package/build/{AudioAnalysis → esm/AudioAnalysis}/extractMelSpectrogram.js +0 -0
  188. /package/build/{AudioAnalysis → esm/AudioAnalysis}/extractPreview.js +0 -0
  189. /package/build/{AudioAnalysis → esm/AudioAnalysis}/extractWaveform.js +0 -0
  190. /package/build/{AudioDeviceManager.js → esm/AudioDeviceManager.js} +0 -0
  191. /package/build/{AudioRecorder.provider.js → esm/AudioRecorder.provider.js} +0 -0
  192. /package/build/{ExpoAudioStream.native.js → esm/ExpoAudioStream.native.js} +0 -0
  193. /package/build/{ExpoAudioStream.types.js → esm/ExpoAudioStream.types.js} +0 -0
  194. /package/build/{ExpoAudioStream.web.js → esm/ExpoAudioStream.web.js} +0 -0
  195. /package/build/{ExpoAudioStreamModule.js → esm/ExpoAudioStreamModule.js} +0 -0
  196. /package/build/{WebRecorder.web.js → esm/WebRecorder.web.js} +0 -0
  197. /package/build/{constants.js → esm/constants.js} +0 -0
  198. /package/build/{events.js → esm/events.js} +0 -0
  199. /package/build/{hooks → esm/hooks}/useAudioDevices.js +0 -0
  200. /package/build/{index.js → esm/index.js} +0 -0
  201. /package/build/{trimAudio.js → esm/trimAudio.js} +0 -0
  202. /package/build/{useAudioRecorder.js → esm/useAudioRecorder.js} +0 -0
  203. /package/build/{utils → esm/utils}/BlobFix.js +0 -0
  204. /package/build/{utils → esm/utils}/audioProcessing.js +0 -0
  205. /package/build/{utils → esm/utils}/concatenateBuffers.js +0 -0
  206. /package/build/{utils → esm/utils}/convertPCMToFloat32.js +0 -0
  207. /package/build/{utils → esm/utils}/crc32.js +0 -0
  208. /package/build/{utils → esm/utils}/encodingToBitDepth.js +0 -0
  209. /package/build/{utils → esm/utils}/getWavFileInfo.js +0 -0
  210. /package/build/{utils → esm/utils}/writeWavHeader.js +0 -0
  211. /package/build/{workers → esm/workers}/InlineFeaturesExtractor.web.js +0 -0
  212. /package/build/{workers → esm/workers}/inlineAudioWebWorker.web.js +0 -0
  213. /package/build/{AudioAnalysis → types/AudioAnalysis}/AudioAnalysis.types.d.ts +0 -0
  214. /package/build/{AudioAnalysis → types/AudioAnalysis}/extractAudioAnalysis.d.ts +0 -0
  215. /package/build/{AudioAnalysis → types/AudioAnalysis}/extractAudioData.d.ts +0 -0
  216. /package/build/{AudioAnalysis → types/AudioAnalysis}/extractMelSpectrogram.d.ts +0 -0
  217. /package/build/{AudioAnalysis → types/AudioAnalysis}/extractPreview.d.ts +0 -0
  218. /package/build/{AudioAnalysis → types/AudioAnalysis}/extractWaveform.d.ts +0 -0
  219. /package/build/{AudioDeviceManager.d.ts → types/AudioDeviceManager.d.ts} +0 -0
  220. /package/build/{AudioRecorder.provider.d.ts → types/AudioRecorder.provider.d.ts} +0 -0
  221. /package/build/{ExpoAudioStream.native.d.ts → types/ExpoAudioStream.native.d.ts} +0 -0
  222. /package/build/{ExpoAudioStream.types.d.ts → types/ExpoAudioStream.types.d.ts} +0 -0
  223. /package/build/{ExpoAudioStream.web.d.ts → types/ExpoAudioStream.web.d.ts} +0 -0
  224. /package/build/{ExpoAudioStreamModule.d.ts → types/ExpoAudioStreamModule.d.ts} +0 -0
  225. /package/build/{WebRecorder.web.d.ts → types/WebRecorder.web.d.ts} +0 -0
  226. /package/build/{constants.d.ts → types/constants.d.ts} +0 -0
  227. /package/build/{events.d.ts → types/events.d.ts} +0 -0
  228. /package/build/{hooks → types/hooks}/useAudioDevices.d.ts +0 -0
  229. /package/build/{index.d.ts → types/index.d.ts} +0 -0
  230. /package/build/{trimAudio.d.ts → types/trimAudio.d.ts} +0 -0
  231. /package/build/{useAudioRecorder.d.ts → types/useAudioRecorder.d.ts} +0 -0
  232. /package/build/{utils → types/utils}/BlobFix.d.ts +0 -0
  233. /package/build/{utils → types/utils}/audioProcessing.d.ts +0 -0
  234. /package/build/{utils → types/utils}/concatenateBuffers.d.ts +0 -0
  235. /package/build/{utils → types/utils}/convertPCMToFloat32.d.ts +0 -0
  236. /package/build/{utils → types/utils}/crc32.d.ts +0 -0
  237. /package/build/{utils → types/utils}/encodingToBitDepth.d.ts +0 -0
  238. /package/build/{utils → types/utils}/getWavFileInfo.d.ts +0 -0
  239. /package/build/{utils → types/utils}/writeWavHeader.d.ts +0 -0
  240. /package/build/{workers → types/workers}/InlineFeaturesExtractor.web.d.ts +0 -0
  241. /package/build/{workers → types/workers}/inlineAudioWebWorker.web.d.ts +0 -0
@@ -0,0 +1,500 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DeviceDisconnectionBehavior = exports.audioDeviceManager = exports.AudioDeviceManager = void 0;
7
+ const expo_modules_core_1 = require("expo-modules-core");
8
+ const react_native_1 = require("react-native");
9
+ const ExpoAudioStream_types_1 = require("./ExpoAudioStream.types");
10
+ Object.defineProperty(exports, "DeviceDisconnectionBehavior", { enumerable: true, get: function () { return ExpoAudioStream_types_1.DeviceDisconnectionBehavior; } });
11
+ const ExpoAudioStreamModule_1 = __importDefault(require("./ExpoAudioStreamModule"));
12
+ // Default device fallback for web and unsupported platforms
13
+ const DEFAULT_DEVICE = {
14
+ id: 'default',
15
+ name: 'Default Microphone',
16
+ type: 'builtin_mic',
17
+ isDefault: true,
18
+ isAvailable: true,
19
+ capabilities: {
20
+ sampleRates: [16000, 44100, 48000],
21
+ channelCounts: [1, 2],
22
+ bitDepths: [16, 24, 32],
23
+ hasEchoCancellation: true,
24
+ hasNoiseSuppression: true,
25
+ hasAutomaticGainControl: true,
26
+ },
27
+ };
28
+ // Helper function to map raw object to AudioDevice interface
29
+ // This handles potential inconsistencies from the native module
30
+ function mapRawDeviceToAudioDevice(rawDevice) {
31
+ const capabilities = rawDevice.capabilities || {};
32
+ return {
33
+ id: rawDevice.id || 'unknown',
34
+ name: rawDevice.name || 'Unknown Device',
35
+ type: rawDevice.type || 'unknown',
36
+ isDefault: rawDevice.isDefault || false,
37
+ isAvailable: rawDevice.isAvailable !== undefined ? rawDevice.isAvailable : true, // Default to true if undefined
38
+ capabilities: {
39
+ sampleRates: capabilities.sampleRates || [16000, 44100, 48000], // Provide defaults
40
+ channelCounts: capabilities.channelCounts || [1, 2],
41
+ bitDepths: capabilities.bitDepths || [16, 24, 32],
42
+ hasEchoCancellation: capabilities.hasEchoCancellation,
43
+ hasNoiseSuppression: capabilities.hasNoiseSuppression,
44
+ hasAutomaticGainControl: capabilities.hasAutomaticGainControl,
45
+ },
46
+ };
47
+ }
48
+ /**
49
+ * Class that provides a cross-platform API for managing audio input devices
50
+ */
51
+ class AudioDeviceManager {
52
+ eventEmitter;
53
+ currentDeviceId = null;
54
+ availableDevices = [];
55
+ deviceChangeListeners = new Set();
56
+ deviceListeners = new Set();
57
+ lastRefreshTime = 0;
58
+ refreshInProgress = false;
59
+ refreshDebounceMs = 500; // Minimum 500ms between refreshes
60
+ logger;
61
+ constructor(options) {
62
+ this.eventEmitter = new expo_modules_core_1.EventEmitter(ExpoAudioStreamModule_1.default);
63
+ this.logger = options?.logger;
64
+ // Listen for device change events from native modules if not on web
65
+ if (react_native_1.Platform.OS !== 'web') {
66
+ // Store the last event type to avoid duplicates
67
+ let lastEventType = null;
68
+ let lastEventTime = 0;
69
+ this.eventEmitter.addListener('deviceChangedEvent', (event) => {
70
+ // Skip processing duplicate events that occur too close together
71
+ const now = Date.now();
72
+ const isSimilarEvent = lastEventType === event.type &&
73
+ now - lastEventTime < this.refreshDebounceMs;
74
+ if (isSimilarEvent) {
75
+ this.logger?.debug(`Skipping similar device event (${event.type}) received too soon`);
76
+ return;
77
+ }
78
+ // Update the last event tracking
79
+ lastEventType = event.type;
80
+ lastEventTime = now;
81
+ // Only refresh on meaningful events
82
+ if (event.type === 'deviceConnected' ||
83
+ event.type === 'deviceDisconnected' ||
84
+ event.type === 'routeChanged') {
85
+ this.logger?.debug(`Processing device event: ${event.type}`);
86
+ // Refresh devices and notify listeners regardless of the direct return value
87
+ this.refreshDevices();
88
+ }
89
+ });
90
+ }
91
+ }
92
+ /**
93
+ * Initialize the device manager with a logger
94
+ * @param logger A logger instance that implements the ConsoleLike interface
95
+ * @returns The manager instance for chaining
96
+ */
97
+ initWithLogger(logger) {
98
+ this.setLogger(logger);
99
+ return this;
100
+ }
101
+ /**
102
+ * Set the logger instance
103
+ * @param logger A logger instance that implements the ConsoleLike interface
104
+ */
105
+ setLogger(logger) {
106
+ this.logger = logger;
107
+ }
108
+ /**
109
+ * Get all available audio input devices
110
+ * @param options Optional settings to force refresh the device list. Can include a refresh flag.
111
+ * @returns Promise resolving to an array of audio devices conforming to AudioDevice interface
112
+ */
113
+ async getAvailableDevices(options) {
114
+ try {
115
+ if (react_native_1.Platform.OS === 'web') {
116
+ this.availableDevices = await this.getWebAudioDevices();
117
+ }
118
+ else if (ExpoAudioStreamModule_1.default.getAvailableInputDevices) {
119
+ // Expecting an array of raw device objects from native
120
+ const rawDevices = await ExpoAudioStreamModule_1.default.getAvailableInputDevices(options);
121
+ // Map raw objects to the AudioDevice interface
122
+ this.availableDevices = rawDevices.map(mapRawDeviceToAudioDevice);
123
+ }
124
+ else {
125
+ // Fallback for unsupported platforms
126
+ this.availableDevices = [DEFAULT_DEVICE];
127
+ }
128
+ return this.availableDevices;
129
+ }
130
+ catch (error) {
131
+ this.logger?.error('Failed to get available devices:', error);
132
+ this.availableDevices = [DEFAULT_DEVICE]; // Ensure state is updated on error
133
+ return this.availableDevices;
134
+ }
135
+ }
136
+ /**
137
+ * Get the currently selected audio input device
138
+ * @returns Promise resolving to the current device (conforming to AudioDevice) or null
139
+ */
140
+ async getCurrentDevice() {
141
+ try {
142
+ if (react_native_1.Platform.OS === 'web') {
143
+ if (!this.currentDeviceId) {
144
+ // On web, return the typed default device if nothing is selected
145
+ return DEFAULT_DEVICE;
146
+ }
147
+ // Refresh web devices to ensure the current one is up-to-date
148
+ const webDevices = await this.getWebAudioDevices();
149
+ return (webDevices.find((d) => d.id === this.currentDeviceId) ||
150
+ DEFAULT_DEVICE // Fallback to default if current ID not found
151
+ );
152
+ }
153
+ else if (ExpoAudioStreamModule_1.default.getCurrentInputDevice) {
154
+ // Expecting a single raw device object or null from native
155
+ const rawDevice = await ExpoAudioStreamModule_1.default.getCurrentInputDevice();
156
+ // Map to AudioDevice interface if not null
157
+ return rawDevice ? mapRawDeviceToAudioDevice(rawDevice) : null;
158
+ }
159
+ else {
160
+ // Fallback for unsupported platforms
161
+ return DEFAULT_DEVICE;
162
+ }
163
+ }
164
+ catch (error) {
165
+ this.logger?.error('Failed to get current device:', error);
166
+ return DEFAULT_DEVICE; // Return default on error
167
+ }
168
+ }
169
+ /**
170
+ * Select a specific audio input device for recording
171
+ * @param deviceId The ID of the device to select
172
+ * @returns Promise resolving to a boolean indicating success
173
+ */
174
+ async selectDevice(deviceId) {
175
+ try {
176
+ let success = false;
177
+ if (react_native_1.Platform.OS === 'web') {
178
+ // Check if the device exists before setting it
179
+ const devices = await this.getWebAudioDevices();
180
+ if (devices.some((d) => d.id === deviceId)) {
181
+ this.currentDeviceId = deviceId;
182
+ success = true;
183
+ }
184
+ else {
185
+ this.logger?.warn(`Web: Device with ID ${deviceId} not found.`);
186
+ success = false;
187
+ }
188
+ }
189
+ else if (ExpoAudioStreamModule_1.default.selectInputDevice) {
190
+ success =
191
+ await ExpoAudioStreamModule_1.default.selectInputDevice(deviceId);
192
+ if (success) {
193
+ this.currentDeviceId = deviceId;
194
+ }
195
+ }
196
+ // Refresh devices after selection attempt to update state
197
+ await this.refreshDevices();
198
+ return success;
199
+ }
200
+ catch (error) {
201
+ this.logger?.error('Failed to select device:', error);
202
+ await this.refreshDevices(); // Refresh even on error
203
+ return false;
204
+ }
205
+ }
206
+ /**
207
+ * Reset to the default audio input device
208
+ * @returns Promise resolving to a boolean indicating success
209
+ */
210
+ async resetToDefaultDevice() {
211
+ try {
212
+ let success = false;
213
+ if (react_native_1.Platform.OS === 'web') {
214
+ this.currentDeviceId = 'default';
215
+ success = true;
216
+ }
217
+ else if (ExpoAudioStreamModule_1.default.resetToDefaultDevice) {
218
+ success = await ExpoAudioStreamModule_1.default.resetToDefaultDevice();
219
+ if (success) {
220
+ this.currentDeviceId = null;
221
+ }
222
+ }
223
+ // Refresh devices after reset attempt
224
+ await this.refreshDevices();
225
+ return success;
226
+ }
227
+ catch (error) {
228
+ this.logger?.error('Failed to reset to default device:', error);
229
+ await this.refreshDevices(); // Refresh even on error
230
+ return false;
231
+ }
232
+ }
233
+ /**
234
+ * Register a listener for device changes
235
+ * @param listener Function to call when devices change (receives AudioDevice[])
236
+ * @returns Function to remove the listener
237
+ */
238
+ addDeviceChangeListener(listener) {
239
+ this.deviceChangeListeners.add(listener);
240
+ // Immediately call listener with current devices if available
241
+ if (this.availableDevices.length > 0) {
242
+ listener([...this.availableDevices]);
243
+ }
244
+ // Return a function to remove the listener
245
+ return () => {
246
+ this.deviceChangeListeners.delete(listener);
247
+ };
248
+ }
249
+ /**
250
+ * Refresh the list of available devices with debouncing and notify listeners.
251
+ * @returns Promise resolving to the updated device list (AudioDevice[])
252
+ */
253
+ async refreshDevices() {
254
+ const now = Date.now();
255
+ if (this.refreshInProgress) {
256
+ this.logger?.debug('Refresh already in progress, skipping');
257
+ return this.availableDevices;
258
+ }
259
+ // Always allow refresh if forced by native event or longer than 2s debounce
260
+ const timeSinceLastRefresh = now - this.lastRefreshTime;
261
+ const shouldDebounce = timeSinceLastRefresh < this.refreshDebounceMs &&
262
+ timeSinceLastRefresh < 2000;
263
+ if (shouldDebounce) {
264
+ this.logger?.debug(`Refresh debounced, skipping (last refresh was ${timeSinceLastRefresh}ms ago)`);
265
+ return this.availableDevices;
266
+ }
267
+ this.logger?.debug('Refreshing devices...');
268
+ this.refreshInProgress = true;
269
+ try {
270
+ // Fetch the latest devices; getAvailableDevices handles mapping now
271
+ const devices = await this.getAvailableDevices({ refresh: true });
272
+ // availableDevices state is updated within getAvailableDevices
273
+ this.notifyListeners(); // Notify listeners with the updated list
274
+ this.lastRefreshTime = Date.now();
275
+ return devices; // Return the fetched & mapped list
276
+ }
277
+ catch (error) {
278
+ this.logger?.error('Error during refreshDevices:', error);
279
+ return this.availableDevices; // Return potentially stale list on error
280
+ }
281
+ finally {
282
+ this.refreshInProgress = false;
283
+ this.logger?.debug('Refresh finished.');
284
+ }
285
+ }
286
+ /**
287
+ * Get audio input devices using the Web Audio API
288
+ * @returns Promise resolving to an array of AudioDevice objects
289
+ */
290
+ async getWebAudioDevices() {
291
+ if (typeof navigator === 'undefined' ||
292
+ !navigator.mediaDevices ||
293
+ !navigator.mediaDevices.enumerateDevices) {
294
+ return [DEFAULT_DEVICE];
295
+ }
296
+ try {
297
+ const permissionStatus = await this.checkMicrophonePermission();
298
+ if (permissionStatus === 'denied') {
299
+ return [
300
+ {
301
+ ...DEFAULT_DEVICE,
302
+ name: 'Microphone Access Denied',
303
+ isAvailable: false,
304
+ },
305
+ ];
306
+ }
307
+ if (permissionStatus !== 'granted') {
308
+ try {
309
+ // Requesting stream often reveals device labels
310
+ await navigator.mediaDevices.getUserMedia({ audio: true });
311
+ }
312
+ catch (error) {
313
+ this.logger?.warn('Microphone permission request failed:', error);
314
+ return [
315
+ {
316
+ ...DEFAULT_DEVICE,
317
+ name: 'Microphone Access Required',
318
+ isAvailable: false,
319
+ },
320
+ ];
321
+ }
322
+ }
323
+ const devices = await navigator.mediaDevices.enumerateDevices();
324
+ const audioInputDevices = devices
325
+ .filter((device) => device.kind === 'audioinput')
326
+ .map((device) => this.mapWebDeviceToAudioDevice(device));
327
+ const hasUnlabeledDevices = audioInputDevices.some((device) => !device.name || device.name.startsWith('Microphone '));
328
+ let finalDevices = audioInputDevices;
329
+ if (hasUnlabeledDevices && this.isSafariOrIOS()) {
330
+ finalDevices = this.enhanceDevicesForSafari(audioInputDevices);
331
+ }
332
+ if (finalDevices.length === 0) {
333
+ finalDevices = [DEFAULT_DEVICE];
334
+ }
335
+ this.setupWebDeviceChangeListener();
336
+ this.availableDevices = finalDevices; // Update internal state
337
+ return finalDevices;
338
+ }
339
+ catch (error) {
340
+ this.logger?.error('Failed to enumerate web audio devices:', error);
341
+ this.availableDevices = [DEFAULT_DEVICE]; // Update state on error
342
+ return this.availableDevices;
343
+ }
344
+ }
345
+ /**
346
+ * Check the current microphone permission status
347
+ * @returns Permission state ('prompt', 'granted', or 'denied')
348
+ */
349
+ async checkMicrophonePermission() {
350
+ if (!navigator.permissions || !navigator.permissions.query) {
351
+ return 'prompt';
352
+ }
353
+ try {
354
+ const permissionStatus = await navigator.permissions.query({
355
+ name: 'microphone',
356
+ });
357
+ permissionStatus.onchange = () => {
358
+ // Refresh devices when permission changes
359
+ this.refreshDevices();
360
+ };
361
+ return permissionStatus.state;
362
+ }
363
+ catch (error) {
364
+ this.logger?.warn('Permission query not supported:', error);
365
+ return 'prompt';
366
+ }
367
+ }
368
+ /**
369
+ * Setup listener for device changes in web environment
370
+ */
371
+ setupWebDeviceChangeListener() {
372
+ if (typeof navigator === 'undefined' ||
373
+ !navigator.mediaDevices ||
374
+ this.deviceListeners.size > 0 // Avoid adding multiple listeners
375
+ ) {
376
+ return;
377
+ }
378
+ const handleDeviceChange = () => {
379
+ this.logger?.debug('Web device change detected.');
380
+ // Refresh devices on change
381
+ this.refreshDevices();
382
+ };
383
+ navigator.mediaDevices.addEventListener('devicechange', handleDeviceChange);
384
+ this.deviceListeners.add(handleDeviceChange);
385
+ this.logger?.debug('Web device change listener added.');
386
+ }
387
+ /**
388
+ * Check if the current browser is Safari or iOS WebKit
389
+ */
390
+ isSafariOrIOS() {
391
+ if (typeof navigator === 'undefined')
392
+ return false;
393
+ const ua = navigator.userAgent;
394
+ return (/^((?!chrome|android).)*safari/i.test(ua) ||
395
+ /iPad|iPhone|iPod/.test(ua) ||
396
+ (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1));
397
+ }
398
+ /**
399
+ * Create enhanced device information for Safari and privacy-restricted browsers
400
+ * @param devices Array of AudioDevice objects, potentially unlabeled
401
+ * @returns Array of enhanced AudioDevice objects
402
+ */
403
+ enhanceDevicesForSafari(devices) {
404
+ const defaultDevice = devices.find((d) => d.isDefault);
405
+ if (devices.length <= 1) {
406
+ // Return a typed default device
407
+ return [
408
+ {
409
+ id: defaultDevice?.id || 'default',
410
+ name: 'Microphone (Browser Managed)',
411
+ type: 'builtin_mic',
412
+ isDefault: true,
413
+ isAvailable: true,
414
+ capabilities: defaultDevice?.capabilities ||
415
+ DEFAULT_DEVICE.capabilities,
416
+ },
417
+ ];
418
+ }
419
+ // Provide more descriptive names for unlabeled devices
420
+ return devices.map((device, index) => {
421
+ if (!device.name || device.name.startsWith('Microphone ')) {
422
+ const deviceTypes = [
423
+ 'Built-in Microphone',
424
+ 'External Microphone',
425
+ 'Headset Microphone',
426
+ ];
427
+ const typeName = deviceTypes[index % deviceTypes.length];
428
+ return {
429
+ ...device,
430
+ name: device.isDefault ? `${typeName} (Default)` : typeName,
431
+ };
432
+ }
433
+ return device;
434
+ });
435
+ }
436
+ /**
437
+ * Map a Web MediaDeviceInfo to our AudioDevice format
438
+ * @param device The MediaDeviceInfo object from the browser
439
+ * @returns An object conforming to the AudioDevice interface
440
+ */
441
+ mapWebDeviceToAudioDevice(device) {
442
+ const isDefault = device.deviceId === 'default';
443
+ const deviceType = this.inferDeviceType(device.label || '');
444
+ // Provide reasonable default capabilities for web devices
445
+ const defaultWebCapabilities = {
446
+ sampleRates: [16000, 44100, 48000],
447
+ channelCounts: [1, 2],
448
+ bitDepths: [16, 32], // Web Audio uses float32, common PCM might be 16/32
449
+ hasEchoCancellation: true, // Often handled by browser
450
+ hasNoiseSuppression: true, // Often handled by browser
451
+ hasAutomaticGainControl: true, // Often handled by browser
452
+ };
453
+ return {
454
+ id: device.deviceId,
455
+ name: device.label || `Microphone ${device.deviceId.substring(0, 8)}`,
456
+ type: deviceType,
457
+ isDefault,
458
+ isAvailable: true, // Assume available if enumerated
459
+ capabilities: defaultWebCapabilities,
460
+ };
461
+ }
462
+ /**
463
+ * Try to infer the device type from its name
464
+ * @param deviceName The label of the device
465
+ * @returns A string representing the inferred device type
466
+ */
467
+ inferDeviceType(deviceName) {
468
+ const name = deviceName.toLowerCase();
469
+ if (name.includes('bluetooth') || name.includes('airpods'))
470
+ return 'bluetooth';
471
+ if (name.includes('usb'))
472
+ return 'usb';
473
+ if (name.includes('headphone') || name.includes('headset')) {
474
+ return name.includes('wired') ? 'wired_headset' : 'wired_headphones';
475
+ }
476
+ if (name.includes('speaker'))
477
+ return 'speaker';
478
+ return 'builtin_mic'; // Default assumption
479
+ }
480
+ /**
481
+ * Notify all registered listeners about device changes.
482
+ */
483
+ notifyListeners() {
484
+ // Pass a copy of the current devices array to listeners
485
+ const devicesCopy = [...this.availableDevices];
486
+ this.logger?.debug(`Notifying ${this.deviceChangeListeners.size} listeners with ${devicesCopy.length} devices.`);
487
+ this.deviceChangeListeners.forEach((listener) => {
488
+ try {
489
+ listener(devicesCopy);
490
+ }
491
+ catch (error) {
492
+ this.logger?.error('Error in device change listener:', error);
493
+ }
494
+ });
495
+ }
496
+ }
497
+ exports.AudioDeviceManager = AudioDeviceManager;
498
+ // Create and export the singleton instance
499
+ exports.audioDeviceManager = new AudioDeviceManager();
500
+ //# sourceMappingURL=AudioDeviceManager.js.map
@@ -0,0 +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 */\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 deviceListeners: Set<() => void> = new Set()\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 constructor(options?: { logger?: ConsoleLike }) {\n this.eventEmitter = new EventEmitter(ExpoAudioStreamModule)\n this.logger = options?.logger\n\n // Listen for device change events from native modules if not on web\n if (Platform.OS !== 'web') {\n // Store the last event type to avoid duplicates\n let lastEventType: string | null = null\n let lastEventTime = 0\n\n this.eventEmitter.addListener(\n 'deviceChangedEvent',\n (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(\n `Processing device event: ${event.type}`\n )\n // Refresh devices and notify listeners regardless of the direct return value\n this.refreshDevices()\n }\n }\n )\n }\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 * 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 * 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.setupWebDeviceChangeListener()\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() {\n if (\n typeof navigator === 'undefined' ||\n !navigator.mediaDevices ||\n this.deviceListeners.size > 0 // Avoid adding multiple listeners\n ) {\n return\n }\n\n const handleDeviceChange = () => {\n this.logger?.debug('Web device change detected.')\n // Refresh devices on change\n this.refreshDevices()\n }\n\n navigator.mediaDevices.addEventListener(\n 'devicechange',\n handleDeviceChange\n )\n this.deviceListeners.add(handleDeviceChange)\n this.logger?.debug('Web device change listener added.')\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 private notifyListeners(): void {\n // Pass a copy of the current devices array to listeners\n const devicesCopy = [...this.availableDevices]\n this.logger?.debug(\n `Notifying ${this.deviceChangeListeners.size} listeners with ${devicesCopy.length} devices.`\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"]}
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.useSharedAudioRecorder = exports.AudioRecorderProvider = void 0;
27
+ // packages/expo-audio-stream/src/AudioRecorder.provider.tsx
28
+ const react_1 = __importStar(require("react"));
29
+ const useAudioRecorder_1 = require("./useAudioRecorder");
30
+ const initContext = {
31
+ isRecording: false,
32
+ isPaused: false,
33
+ durationMs: 0,
34
+ size: 0,
35
+ compression: undefined,
36
+ startRecording: async () => {
37
+ throw new Error('AudioRecorderProvider not found');
38
+ },
39
+ stopRecording: async () => {
40
+ throw new Error('AudioRecorderProvider not found');
41
+ },
42
+ pauseRecording: async () => {
43
+ throw new Error('AudioRecorderProvider not found');
44
+ },
45
+ resumeRecording: async () => {
46
+ throw new Error('AudioRecorderProvider not found');
47
+ },
48
+ prepareRecording: async () => {
49
+ throw new Error('AudioRecorderProvider not found');
50
+ },
51
+ };
52
+ const AudioRecorderContext = (0, react_1.createContext)(initContext);
53
+ const AudioRecorderProvider = ({ children, config = {}, }) => {
54
+ const audioRecorder = (0, useAudioRecorder_1.useAudioRecorder)(config);
55
+ return (<AudioRecorderContext.Provider value={audioRecorder}>
56
+ {children}
57
+ </AudioRecorderContext.Provider>);
58
+ };
59
+ exports.AudioRecorderProvider = AudioRecorderProvider;
60
+ const useSharedAudioRecorder = () => {
61
+ const context = (0, react_1.useContext)(AudioRecorderContext);
62
+ if (!context) {
63
+ throw new Error('useSharedAudioRecorder must be used within an AudioRecorderProvider');
64
+ }
65
+ return context;
66
+ };
67
+ exports.useSharedAudioRecorder = useSharedAudioRecorder;
68
+ //# sourceMappingURL=AudioRecorder.provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioRecorder.provider.js","sourceRoot":"","sources":["../../src/AudioRecorder.provider.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,4DAA4D;AAC5D,+CAAwD;AAGxD,yDAA4E;AAE5E,MAAM,WAAW,GAA0B;IACvC,WAAW,EAAE,KAAK;IAClB,QAAQ,EAAE,KAAK;IACf,UAAU,EAAE,CAAC;IACb,IAAI,EAAE,CAAC;IACP,WAAW,EAAE,SAAS;IACtB,cAAc,EAAE,KAAK,IAAI,EAAE;QACvB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IACtD,CAAC;IACD,aAAa,EAAE,KAAK,IAAI,EAAE;QACtB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IACtD,CAAC;IACD,cAAc,EAAE,KAAK,IAAI,EAAE;QACvB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IACtD,CAAC;IACD,eAAe,EAAE,KAAK,IAAI,EAAE;QACxB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IACtD,CAAC;IACD,gBAAgB,EAAE,KAAK,IAAI,EAAE;QACzB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IACtD,CAAC;CACJ,CAAA;AAED,MAAM,oBAAoB,GAAG,IAAA,qBAAa,EAAwB,WAAW,CAAC,CAAA;AAOvE,MAAM,qBAAqB,GAAyC,CAAC,EACxE,QAAQ,EACR,MAAM,GAAG,EAAE,GACd,EAAE,EAAE;IACD,MAAM,aAAa,GAAG,IAAA,mCAAgB,EAAC,MAAM,CAAC,CAAA;IAC9C,OAAO,CACH,CAAC,oBAAoB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,CAChD;YAAA,CAAC,QAAQ,CACb;QAAA,EAAE,oBAAoB,CAAC,QAAQ,CAAC,CACnC,CAAA;AACL,CAAC,CAAA;AAVY,QAAA,qBAAqB,yBAUjC;AAEM,MAAM,sBAAsB,GAAG,GAAG,EAAE;IACvC,MAAM,OAAO,GAAG,IAAA,kBAAU,EAAC,oBAAoB,CAAC,CAAA;IAChD,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACX,qEAAqE,CACxE,CAAA;IACL,CAAC;IACD,OAAO,OAAO,CAAA;AAClB,CAAC,CAAA;AARY,QAAA,sBAAsB,0BAQlC","sourcesContent":["// packages/expo-audio-stream/src/AudioRecorder.provider.tsx\nimport React, { createContext, useContext } from 'react'\n\nimport { UseAudioRecorderState } from './ExpoAudioStream.types'\nimport { UseAudioRecorderProps, useAudioRecorder } from './useAudioRecorder'\n\nconst initContext: UseAudioRecorderState = {\n isRecording: false,\n isPaused: false,\n durationMs: 0,\n size: 0,\n compression: undefined,\n startRecording: async () => {\n throw new Error('AudioRecorderProvider not found')\n },\n stopRecording: async () => {\n throw new Error('AudioRecorderProvider not found')\n },\n pauseRecording: async () => {\n throw new Error('AudioRecorderProvider not found')\n },\n resumeRecording: async () => {\n throw new Error('AudioRecorderProvider not found')\n },\n prepareRecording: async () => {\n throw new Error('AudioRecorderProvider not found')\n },\n}\n\nconst AudioRecorderContext = createContext<UseAudioRecorderState>(initContext)\n\ninterface AudioRecorderProviderProps {\n children: React.ReactNode\n config?: UseAudioRecorderProps\n}\n\nexport const AudioRecorderProvider: React.FC<AudioRecorderProviderProps> = ({\n children,\n config = {},\n}) => {\n const audioRecorder = useAudioRecorder(config)\n return (\n <AudioRecorderContext.Provider value={audioRecorder}>\n {children}\n </AudioRecorderContext.Provider>\n )\n}\n\nexport const useSharedAudioRecorder = () => {\n const context = useContext(AudioRecorderContext)\n if (!context) {\n throw new Error(\n 'useSharedAudioRecorder must be used within an AudioRecorderProvider'\n )\n }\n return context\n}\n"]}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ // src/ExpoAudioStreamModule.ts
4
+ const expo_modules_core_1 = require("expo-modules-core");
5
+ // It loads the native module object from the JSI or falls back to
6
+ // the bridge module (from NativeModulesProxy) if the remote debugger is on.
7
+ exports.default = (0, expo_modules_core_1.requireNativeModule)('ExpoAudioStream');
8
+ //# sourceMappingURL=ExpoAudioStream.native.js.map