@siteed/expo-audio-stream 1.0.3 → 1.1.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 (132) hide show
  1. package/README.md +26 -175
  2. package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +47 -7
  3. package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +1 -0
  4. package/android/src/main/java/net/siteed/audiostream/Constants.kt +5 -0
  5. package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +12 -3
  6. package/build/index.js +8 -7
  7. package/ios/AudioProcessor.swift +7 -5
  8. package/ios/AudioStreamManager.swift +1 -0
  9. package/ios/ExpoAudioStream.podspec +1 -1
  10. package/ios/ExpoAudioStreamModule.swift +36 -0
  11. package/ios/RecordingResult.swift +1 -0
  12. package/package.json +95 -65
  13. package/src/AudioAnalysis/AudioAnalysis.types.ts +59 -60
  14. package/src/AudioAnalysis/extractAudioAnalysis.ts +132 -121
  15. package/src/AudioAnalysis/extractWaveform.ts +18 -18
  16. package/src/AudioRecorder.provider.tsx +53 -53
  17. package/src/ExpoAudioStream.native.ts +2 -2
  18. package/src/ExpoAudioStream.types.ts +59 -53
  19. package/src/ExpoAudioStream.web.ts +231 -205
  20. package/src/ExpoAudioStreamModule.ts +22 -15
  21. package/src/WebRecorder.web.ts +407 -390
  22. package/src/constants.ts +11 -11
  23. package/src/events.ts +27 -13
  24. package/src/index.ts +17 -15
  25. package/src/logger.ts +15 -19
  26. package/src/useAudioRecorder.tsx +394 -389
  27. package/src/utils/BlobFix.ts +550 -0
  28. package/src/utils/concatenateBuffers.ts +24 -0
  29. package/src/utils/convertPCMToFloat32.ts +72 -45
  30. package/src/utils/encodingToBitDepth.ts +14 -14
  31. package/src/utils/getWavFileInfo.ts +106 -99
  32. package/src/utils/writeWavHeader.ts +50 -45
  33. package/src/workers/InlineFeaturesExtractor.web.tsx +296 -286
  34. package/src/workers/inlineAudioWebWorker.web.tsx +230 -222
  35. package/.eslintrc.js +0 -2
  36. package/.size-limit.json +0 -6
  37. package/android/.gradle/8.1.1/checksums/checksums.lock +0 -0
  38. package/android/.gradle/8.1.1/dependencies-accessors/dependencies-accessors.lock +0 -0
  39. package/android/.gradle/8.1.1/dependencies-accessors/gc.properties +0 -0
  40. package/android/.gradle/8.1.1/fileChanges/last-build.bin +0 -0
  41. package/android/.gradle/8.1.1/fileHashes/fileHashes.lock +0 -0
  42. package/android/.gradle/8.1.1/gc.properties +0 -0
  43. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  44. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  45. package/android/.gradle/vcs-1/gc.properties +0 -0
  46. package/app.plugin.js +0 -1
  47. package/build/AudioAnalysis/AudioAnalysis.types.d.ts +0 -76
  48. package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +0 -1
  49. package/build/AudioAnalysis/AudioAnalysis.types.js +0 -3
  50. package/build/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
  51. package/build/AudioAnalysis/extractAudioAnalysis.d.ts +0 -4
  52. package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +0 -1
  53. package/build/AudioAnalysis/extractAudioAnalysis.js +0 -101
  54. package/build/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
  55. package/build/AudioAnalysis/extractWaveform.d.ts +0 -8
  56. package/build/AudioAnalysis/extractWaveform.d.ts.map +0 -1
  57. package/build/AudioAnalysis/extractWaveform.js +0 -14
  58. package/build/AudioAnalysis/extractWaveform.js.map +0 -1
  59. package/build/AudioRecorder.provider.d.ts +0 -23
  60. package/build/AudioRecorder.provider.d.ts.map +0 -1
  61. package/build/AudioRecorder.provider.js +0 -36
  62. package/build/AudioRecorder.provider.js.map +0 -1
  63. package/build/ExpoAudioStream.native.d.ts +0 -3
  64. package/build/ExpoAudioStream.native.d.ts.map +0 -1
  65. package/build/ExpoAudioStream.native.js +0 -6
  66. package/build/ExpoAudioStream.native.js.map +0 -1
  67. package/build/ExpoAudioStream.types.d.ts +0 -60
  68. package/build/ExpoAudioStream.types.d.ts.map +0 -1
  69. package/build/ExpoAudioStream.types.js +0 -2
  70. package/build/ExpoAudioStream.types.js.map +0 -1
  71. package/build/ExpoAudioStream.web.d.ts +0 -42
  72. package/build/ExpoAudioStream.web.d.ts.map +0 -1
  73. package/build/ExpoAudioStream.web.js +0 -185
  74. package/build/ExpoAudioStream.web.js.map +0 -1
  75. package/build/ExpoAudioStreamModule.d.ts +0 -3
  76. package/build/ExpoAudioStreamModule.d.ts.map +0 -1
  77. package/build/ExpoAudioStreamModule.js +0 -18
  78. package/build/ExpoAudioStreamModule.js.map +0 -1
  79. package/build/WebRecorder.web.d.ts +0 -51
  80. package/build/WebRecorder.web.d.ts.map +0 -1
  81. package/build/WebRecorder.web.js +0 -288
  82. package/build/WebRecorder.web.js.map +0 -1
  83. package/build/constants.d.ts +0 -11
  84. package/build/constants.d.ts.map +0 -1
  85. package/build/constants.js +0 -14
  86. package/build/constants.js.map +0 -1
  87. package/build/events.d.ts +0 -6
  88. package/build/events.d.ts.map +0 -1
  89. package/build/events.js +0 -15
  90. package/build/events.js.map +0 -1
  91. package/build/index.d.ts +0 -10
  92. package/build/index.d.ts.map +0 -1
  93. package/build/index.js.map +0 -1
  94. package/build/logger.d.ts +0 -9
  95. package/build/logger.d.ts.map +0 -1
  96. package/build/logger.js +0 -17
  97. package/build/logger.js.map +0 -1
  98. package/build/useAudioRecorder.d.ts +0 -37
  99. package/build/useAudioRecorder.d.ts.map +0 -1
  100. package/build/useAudioRecorder.js +0 -271
  101. package/build/useAudioRecorder.js.map +0 -1
  102. package/build/utils/convertPCMToFloat32.d.ts +0 -11
  103. package/build/utils/convertPCMToFloat32.d.ts.map +0 -1
  104. package/build/utils/convertPCMToFloat32.js +0 -41
  105. package/build/utils/convertPCMToFloat32.js.map +0 -1
  106. package/build/utils/encodingToBitDepth.d.ts +0 -5
  107. package/build/utils/encodingToBitDepth.d.ts.map +0 -1
  108. package/build/utils/encodingToBitDepth.js +0 -13
  109. package/build/utils/encodingToBitDepth.js.map +0 -1
  110. package/build/utils/getWavFileInfo.d.ts +0 -25
  111. package/build/utils/getWavFileInfo.d.ts.map +0 -1
  112. package/build/utils/getWavFileInfo.js +0 -89
  113. package/build/utils/getWavFileInfo.js.map +0 -1
  114. package/build/utils/writeWavHeader.d.ts +0 -9
  115. package/build/utils/writeWavHeader.d.ts.map +0 -1
  116. package/build/utils/writeWavHeader.js +0 -41
  117. package/build/utils/writeWavHeader.js.map +0 -1
  118. package/build/workers/InlineFeaturesExtractor.web.d.ts +0 -2
  119. package/build/workers/InlineFeaturesExtractor.web.d.ts.map +0 -1
  120. package/build/workers/InlineFeaturesExtractor.web.js +0 -303
  121. package/build/workers/InlineFeaturesExtractor.web.js.map +0 -1
  122. package/build/workers/inlineAudioWebWorker.web.d.ts +0 -2
  123. package/build/workers/inlineAudioWebWorker.web.d.ts.map +0 -1
  124. package/build/workers/inlineAudioWebWorker.web.js +0 -243
  125. package/build/workers/inlineAudioWebWorker.web.js.map +0 -1
  126. package/expo-module.config.json +0 -18
  127. package/plugin/build/index.d.ts +0 -5
  128. package/plugin/build/index.js +0 -28
  129. package/plugin/src/index.ts +0 -53
  130. package/plugin/tsconfig.json +0 -14
  131. package/publish.sh +0 -8
  132. package/tsconfig.json +0 -9
@@ -1,271 +0,0 @@
1
- // src/useAudioRecorder.ts
2
- import { Platform } from "expo-modules-core";
3
- import { useCallback, useEffect, useReducer, useRef } from "react";
4
- import ExpoAudioStreamModule from "./ExpoAudioStreamModule";
5
- import { addAudioAnalysisListener, addAudioEventListener } from "./events";
6
- import { disableAllLoggers, enableAllLoggers, getLogger } from "./logger";
7
- const TAG = "useAudioRecorder";
8
- const logger = getLogger(TAG);
9
- const defaultAnalysis = {
10
- pointsPerSecond: 10,
11
- bitDepth: 32,
12
- numberOfChannels: 1,
13
- durationMs: 0,
14
- sampleRate: 44100,
15
- samples: 0,
16
- dataPoints: [],
17
- amplitudeRange: {
18
- min: Number.POSITIVE_INFINITY,
19
- max: Number.NEGATIVE_INFINITY,
20
- },
21
- };
22
- function audioRecorderReducer(state, action) {
23
- switch (action.type) {
24
- case "START":
25
- return {
26
- ...state,
27
- isRecording: true,
28
- isPaused: false,
29
- durationMs: 0,
30
- size: 0,
31
- analysisData: defaultAnalysis, // Reset analysis data
32
- };
33
- case "STOP":
34
- return { ...state, isRecording: false, isPaused: false };
35
- case "PAUSE":
36
- return { ...state, isPaused: true, isRecording: false };
37
- case "RESUME":
38
- return { ...state, isPaused: false, isRecording: true };
39
- case "UPDATE_STATUS":
40
- return {
41
- ...state,
42
- durationMs: action.payload.durationMs,
43
- size: action.payload.size,
44
- };
45
- case "UPDATE_ANALYSIS":
46
- return {
47
- ...state,
48
- analysisData: action.payload,
49
- };
50
- default:
51
- return state;
52
- }
53
- }
54
- export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtratorUrl, } = {}) {
55
- const [state, dispatch] = useReducer(audioRecorderReducer, {
56
- isRecording: false,
57
- isPaused: false,
58
- durationMs: 0,
59
- size: 0,
60
- analysisData: undefined,
61
- });
62
- const analysisListenerRef = useRef(null);
63
- const analysisRef = useRef({ ...defaultAnalysis });
64
- // Instantiate the module for web with URLs
65
- const ExpoAudioStream = Platform.OS === "web"
66
- ? ExpoAudioStreamModule({ audioWorkletUrl, featuresExtratorUrl })
67
- : ExpoAudioStreamModule;
68
- const onAudioStreamRef = useRef(null);
69
- const handleAudioAnalysis = useCallback(async ({ analysis, visualizationDuration }) => {
70
- const savedAnalysisData = analysisRef.current || { ...defaultAnalysis };
71
- const maxDuration = visualizationDuration;
72
- logger.debug(`[handleAudioAnalysis] Received audio analysis: maxDuration=${maxDuration} analysis.dataPoints=${analysis.dataPoints.length} analysisData.dataPoints=${savedAnalysisData.dataPoints.length}`, analysis);
73
- // Combine data points
74
- const combinedDataPoints = [
75
- ...savedAnalysisData.dataPoints,
76
- ...analysis.dataPoints,
77
- ];
78
- // Calculate the new duration
79
- const pointsPerSecond = analysis.pointsPerSecond || savedAnalysisData.pointsPerSecond;
80
- const maxDataPoints = (pointsPerSecond * visualizationDuration) / 1000;
81
- logger.debug(`[handleAudioAnalysis] Combined data points before trimming: pointsPerSecond=${pointsPerSecond} visualizationDuration=${visualizationDuration} combinedDataPointsLength=${combinedDataPoints.length} vs maxDataPoints=${maxDataPoints}`);
82
- // Trim data points to keep within the maximum number of data points
83
- if (combinedDataPoints.length > maxDataPoints) {
84
- combinedDataPoints.splice(0, combinedDataPoints.length - maxDataPoints);
85
- }
86
- savedAnalysisData.dataPoints = combinedDataPoints;
87
- savedAnalysisData.bitDepth =
88
- analysis.bitDepth || savedAnalysisData.bitDepth;
89
- savedAnalysisData.durationMs =
90
- combinedDataPoints.length * (1000 / pointsPerSecond);
91
- // Update amplitude range
92
- const newMin = Math.min(savedAnalysisData.amplitudeRange.min, analysis.amplitudeRange.min);
93
- const newMax = Math.max(savedAnalysisData.amplitudeRange.max, analysis.amplitudeRange.max);
94
- savedAnalysisData.amplitudeRange = {
95
- min: newMin,
96
- max: newMax,
97
- };
98
- logger.debug(`[handleAudioAnalysis] Updated analysis data: durationMs=${savedAnalysisData.durationMs}`, savedAnalysisData);
99
- // Update the ref
100
- analysisRef.current = savedAnalysisData;
101
- // Dispatch the updated analysis data to state to trigger re-render
102
- // need to use spread operator otherwise it doesnt trigger update.
103
- dispatch({ type: "UPDATE_ANALYSIS", payload: { ...savedAnalysisData } });
104
- }, [dispatch]);
105
- const handleAudioEvent = useCallback(async (eventData) => {
106
- const { fileUri, deltaSize, totalSize, lastEmittedSize, position, streamUuid, encoded, mimeType, buffer, } = eventData;
107
- logger.debug(`[handleAudioEvent] Received audio event:`, {
108
- fileUri,
109
- deltaSize,
110
- totalSize,
111
- position,
112
- mimeType,
113
- lastEmittedSize,
114
- streamUuid,
115
- encodedLength: encoded?.length,
116
- });
117
- if (deltaSize === 0) {
118
- // Ignore packet with no data
119
- return;
120
- }
121
- try {
122
- // Coming from native ( ios / android ) otherwise buffer is set
123
- if (Platform.OS !== "web") {
124
- // Read the audio file as a base64 string for comparison
125
- if (!encoded) {
126
- console.error(`${TAG} Encoded audio data is missing`);
127
- throw new Error("Encoded audio data is missing");
128
- }
129
- onAudioStreamRef.current?.({
130
- data: encoded,
131
- position,
132
- fileUri,
133
- eventDataSize: deltaSize,
134
- totalSize,
135
- });
136
- }
137
- else if (buffer) {
138
- // Coming from web
139
- const webEvent = {
140
- data: buffer,
141
- position,
142
- fileUri,
143
- eventDataSize: deltaSize,
144
- totalSize,
145
- };
146
- onAudioStreamRef.current?.(webEvent);
147
- logger.debug(`[handleAudioEvent] Audio data sent to onAudioStream`, webEvent);
148
- }
149
- }
150
- catch (error) {
151
- console.error(`${TAG} Error processing audio event:`, error);
152
- }
153
- }, []);
154
- const checkStatus = useCallback(async () => {
155
- try {
156
- if (!state.isRecording) {
157
- logger.debug(`Not recording, exiting status check.`);
158
- return;
159
- }
160
- const status = ExpoAudioStream.status();
161
- if (debug) {
162
- logger.debug(`Status:`, status);
163
- }
164
- dispatch({
165
- type: "UPDATE_STATUS",
166
- payload: { durationMs: status.durationMs, size: status.size },
167
- });
168
- }
169
- catch (error) {
170
- console.error(`${TAG} Error getting status:`, error);
171
- }
172
- }, [state.isRecording]);
173
- const startRecording = useCallback(async (recordingOptions) => {
174
- logger.debug(`start recoding`, recordingOptions);
175
- analysisRef.current = { ...defaultAnalysis }; // Reset analysis data
176
- const { onAudioStream, ...options } = recordingOptions;
177
- const { enableProcessing } = options;
178
- const maxRecentDataDuration = 10000; // TODO compute maxRecentDataDuration based on screen dimensions
179
- if (typeof onAudioStream === "function") {
180
- onAudioStreamRef.current = onAudioStream;
181
- }
182
- else {
183
- console.warn(`${TAG} onAudioStream is not a function`, onAudioStream);
184
- onAudioStreamRef.current = null;
185
- }
186
- const startResult = await ExpoAudioStream.startRecording(options);
187
- dispatch({ type: "START" });
188
- if (enableProcessing) {
189
- logger.debug(`Enabling audio analysis listener`);
190
- const listener = addAudioAnalysisListener(async (analysisData) => {
191
- try {
192
- await handleAudioAnalysis({
193
- analysis: analysisData,
194
- visualizationDuration: maxRecentDataDuration,
195
- });
196
- }
197
- catch (error) {
198
- console.warn(`${TAG} Error processing audio analysis:`, error);
199
- }
200
- });
201
- analysisListenerRef.current = listener;
202
- }
203
- return startResult;
204
- }, [handleAudioAnalysis, dispatch]);
205
- const stopRecording = useCallback(async () => {
206
- logger.debug(`stoping recording`);
207
- if (analysisListenerRef.current) {
208
- analysisListenerRef.current.remove();
209
- analysisListenerRef.current = null;
210
- }
211
- const stopResult = await ExpoAudioStream.stopRecording();
212
- onAudioStreamRef.current = null;
213
- logger.debug(`recording stopped`, stopResult);
214
- dispatch({ type: "STOP" });
215
- return stopResult;
216
- }, [dispatch]);
217
- const pauseRecording = useCallback(async () => {
218
- logger.debug(`pause recording`);
219
- const pauseResult = await ExpoAudioStream.pauseRecording();
220
- dispatch({ type: "PAUSE" });
221
- return pauseResult;
222
- }, [dispatch]);
223
- const resumeRecording = useCallback(async () => {
224
- logger.debug(`resume recording`);
225
- const resumeResult = await ExpoAudioStream.resumeRecording();
226
- dispatch({ type: "RESUME" });
227
- return resumeResult;
228
- }, [dispatch]);
229
- useEffect(() => {
230
- let interval;
231
- if (state.isRecording) {
232
- interval = setInterval(checkStatus, 1000);
233
- }
234
- return () => {
235
- if (interval) {
236
- clearInterval(interval);
237
- }
238
- };
239
- }, [checkStatus, state.isRecording]);
240
- useEffect(() => {
241
- logger.debug(`Registering audio event listener`);
242
- const subscribeAudio = addAudioEventListener(handleAudioEvent);
243
- logger.debug(`Subscribed to audio event listener and analysis listener`, {
244
- subscribeAudio,
245
- });
246
- return () => {
247
- logger.debug(`Removing audio event listener`);
248
- subscribeAudio.remove();
249
- };
250
- }, [handleAudioEvent, handleAudioAnalysis]);
251
- useEffect(() => {
252
- if (debug) {
253
- enableAllLoggers();
254
- }
255
- else {
256
- disableAllLoggers();
257
- }
258
- }, [debug]);
259
- return {
260
- startRecording,
261
- stopRecording,
262
- pauseRecording,
263
- resumeRecording,
264
- isPaused: state.isPaused,
265
- isRecording: state.isRecording,
266
- durationMs: state.durationMs,
267
- size: state.size,
268
- analysisData: state.analysisData,
269
- };
270
- }
271
- //# sourceMappingURL=useAudioRecorder.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useAudioRecorder.js","sourceRoot":"","sources":["../src/useAudioRecorder.tsx"],"names":[],"mappings":"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","sourcesContent":["// src/useAudioRecorder.ts\nimport { Platform, Subscription } from \"expo-modules-core\";\nimport { useCallback, useEffect, useReducer, useRef } from \"react\";\n\nimport {\n AudioAnalysisData,\n AudioAnalysisEventPayload,\n AudioFeaturesOptions,\n} from \"./AudioAnalysis/AudioAnalysis.types\";\nimport {\n AudioDataEvent,\n AudioEventPayload,\n AudioRecordingResult,\n AudioStreamStatus,\n RecordingConfig,\n StartRecordingResult,\n} from \"./ExpoAudioStream.types\";\nimport ExpoAudioStreamModule from \"./ExpoAudioStreamModule\";\nimport { addAudioAnalysisListener, addAudioEventListener } from \"./events\";\nimport { disableAllLoggers, enableAllLoggers, getLogger } from \"./logger\";\nimport { WavFileInfo } from \"./utils/getWavFileInfo\";\n\nconst TAG = \"useAudioRecorder\";\nconst logger = getLogger(TAG);\n\nexport interface ExtractMetadataProps {\n fileUri?: string; // should provide either fileUri or arrayBuffer\n wavMetadata?: WavFileInfo;\n arrayBuffer?: ArrayBuffer;\n bitDepth?: number;\n skipWavHeader?: boolean;\n durationMs?: number;\n sampleRate?: number;\n numberOfChannels?: number;\n algorithm?: \"peak\" | \"rms\";\n position?: number; // Optional number of bytes to skip. Default is 0\n length?: number; // Optional number of bytes to read.\n pointsPerSecond?: number; // Optional number of points per second. Use to reduce the number of points and compute the number of datapoints to return.\n features?: AudioFeaturesOptions;\n featuresExtratorUrl?: string;\n}\n\nexport interface UseAudioRecorderProps {\n debug?: boolean;\n audioWorkletUrl?: string;\n featuresExtratorUrl?: string;\n}\n\nexport interface UseAudioRecorderState {\n startRecording: (_: RecordingConfig) => Promise<StartRecordingResult>;\n stopRecording: () => Promise<AudioRecordingResult | null>;\n pauseRecording: () => void;\n resumeRecording: () => void;\n isRecording: boolean;\n isPaused: boolean;\n durationMs: number; // Duration of the recording\n size: number; // Size in bytes of the recorded audio\n analysisData?: AudioAnalysisData;\n}\n\ninterface RecorderReducerState {\n isRecording: boolean;\n isPaused: boolean;\n durationMs: number;\n size: number;\n analysisData?: AudioAnalysisData;\n}\n\ntype RecorderAction =\n | { type: \"START\" | \"STOP\" | \"PAUSE\" | \"RESUME\" }\n | { type: \"UPDATE_STATUS\"; payload: { durationMs: number; size: number } }\n | { type: \"UPDATE_ANALYSIS\"; payload: AudioAnalysisData };\n\nconst defaultAnalysis: AudioAnalysisData = {\n pointsPerSecond: 10,\n bitDepth: 32,\n numberOfChannels: 1,\n durationMs: 0,\n sampleRate: 44100,\n samples: 0,\n dataPoints: [],\n amplitudeRange: {\n min: Number.POSITIVE_INFINITY,\n max: Number.NEGATIVE_INFINITY,\n },\n};\n\nfunction audioRecorderReducer(\n state: RecorderReducerState,\n action: RecorderAction,\n): RecorderReducerState {\n switch (action.type) {\n case \"START\":\n return {\n ...state,\n isRecording: true,\n isPaused: false,\n durationMs: 0,\n size: 0,\n analysisData: defaultAnalysis, // Reset analysis data\n };\n case \"STOP\":\n return { ...state, isRecording: false, isPaused: false };\n case \"PAUSE\":\n return { ...state, isPaused: true, isRecording: false };\n case \"RESUME\":\n return { ...state, isPaused: false, isRecording: true };\n case \"UPDATE_STATUS\":\n return {\n ...state,\n durationMs: action.payload.durationMs,\n size: action.payload.size,\n };\n case \"UPDATE_ANALYSIS\":\n return {\n ...state,\n analysisData: action.payload,\n };\n default:\n return state;\n }\n}\n\nexport function useAudioRecorder({\n debug = false,\n audioWorkletUrl,\n featuresExtratorUrl,\n}: UseAudioRecorderProps = {}): UseAudioRecorderState {\n const [state, dispatch] = useReducer(audioRecorderReducer, {\n isRecording: false,\n isPaused: false,\n durationMs: 0,\n size: 0,\n analysisData: undefined,\n });\n\n const analysisListenerRef = useRef<Subscription | null>(null);\n const analysisRef = useRef<AudioAnalysisData>({ ...defaultAnalysis });\n\n // Instantiate the module for web with URLs\n const ExpoAudioStream =\n Platform.OS === \"web\"\n ? ExpoAudioStreamModule({ audioWorkletUrl, featuresExtratorUrl })\n : ExpoAudioStreamModule;\n\n const onAudioStreamRef = useRef<\n ((_: AudioDataEvent) => Promise<void>) | null\n >(null);\n\n const handleAudioAnalysis = useCallback(\n async ({ analysis, visualizationDuration }: AudioAnalysisEventPayload) => {\n const savedAnalysisData = analysisRef.current || { ...defaultAnalysis };\n\n const maxDuration = visualizationDuration;\n\n logger.debug(\n `[handleAudioAnalysis] Received audio analysis: maxDuration=${maxDuration} analysis.dataPoints=${analysis.dataPoints.length} analysisData.dataPoints=${savedAnalysisData.dataPoints.length}`,\n analysis,\n );\n\n // Combine data points\n const combinedDataPoints = [\n ...savedAnalysisData.dataPoints,\n ...analysis.dataPoints,\n ];\n\n // Calculate the new duration\n const pointsPerSecond =\n analysis.pointsPerSecond || savedAnalysisData.pointsPerSecond;\n const maxDataPoints = (pointsPerSecond * visualizationDuration) / 1000;\n\n logger.debug(\n `[handleAudioAnalysis] Combined data points before trimming: pointsPerSecond=${pointsPerSecond} visualizationDuration=${visualizationDuration} combinedDataPointsLength=${combinedDataPoints.length} vs maxDataPoints=${maxDataPoints}`,\n );\n\n // Trim data points to keep within the maximum number of data points\n if (combinedDataPoints.length > maxDataPoints) {\n combinedDataPoints.splice(0, combinedDataPoints.length - maxDataPoints);\n }\n\n savedAnalysisData.dataPoints = combinedDataPoints;\n savedAnalysisData.bitDepth =\n analysis.bitDepth || savedAnalysisData.bitDepth;\n savedAnalysisData.durationMs =\n combinedDataPoints.length * (1000 / pointsPerSecond);\n\n // Update amplitude range\n const newMin = Math.min(\n savedAnalysisData.amplitudeRange.min,\n analysis.amplitudeRange.min,\n );\n const newMax = Math.max(\n savedAnalysisData.amplitudeRange.max,\n analysis.amplitudeRange.max,\n );\n\n savedAnalysisData.amplitudeRange = {\n min: newMin,\n max: newMax,\n };\n\n logger.debug(\n `[handleAudioAnalysis] Updated analysis data: durationMs=${savedAnalysisData.durationMs}`,\n savedAnalysisData,\n );\n\n // Update the ref\n analysisRef.current = savedAnalysisData;\n\n // Dispatch the updated analysis data to state to trigger re-render\n // need to use spread operator otherwise it doesnt trigger update.\n dispatch({ type: \"UPDATE_ANALYSIS\", payload: { ...savedAnalysisData } });\n },\n [dispatch],\n );\n\n const handleAudioEvent = useCallback(async (eventData: AudioEventPayload) => {\n const {\n fileUri,\n deltaSize,\n totalSize,\n lastEmittedSize,\n position,\n streamUuid,\n encoded,\n mimeType,\n buffer,\n } = eventData;\n logger.debug(`[handleAudioEvent] Received audio event:`, {\n fileUri,\n deltaSize,\n totalSize,\n position,\n mimeType,\n lastEmittedSize,\n streamUuid,\n encodedLength: encoded?.length,\n });\n if (deltaSize === 0) {\n // Ignore packet with no data\n return;\n }\n try {\n // Coming from native ( ios / android ) otherwise buffer is set\n if (Platform.OS !== \"web\") {\n // Read the audio file as a base64 string for comparison\n if (!encoded) {\n console.error(`${TAG} Encoded audio data is missing`);\n throw new Error(\"Encoded audio data is missing\");\n }\n onAudioStreamRef.current?.({\n data: encoded,\n position,\n fileUri,\n eventDataSize: deltaSize,\n totalSize,\n });\n } else if (buffer) {\n // Coming from web\n const webEvent: AudioDataEvent = {\n data: buffer,\n position,\n fileUri,\n eventDataSize: deltaSize,\n totalSize,\n };\n onAudioStreamRef.current?.(webEvent);\n logger.debug(\n `[handleAudioEvent] Audio data sent to onAudioStream`,\n webEvent,\n );\n }\n } catch (error) {\n console.error(`${TAG} Error processing audio event:`, error);\n }\n }, []);\n\n const checkStatus = useCallback(async () => {\n try {\n if (!state.isRecording) {\n logger.debug(`Not recording, exiting status check.`);\n return;\n }\n\n const status: AudioStreamStatus = ExpoAudioStream.status();\n if (debug) {\n logger.debug(`Status:`, status);\n }\n\n dispatch({\n type: \"UPDATE_STATUS\",\n payload: { durationMs: status.durationMs, size: status.size },\n });\n } catch (error) {\n console.error(`${TAG} Error getting status:`, error);\n }\n }, [state.isRecording]);\n\n const startRecording = useCallback(\n async (recordingOptions: RecordingConfig) => {\n logger.debug(`start recoding`, recordingOptions);\n\n analysisRef.current = { ...defaultAnalysis }; // Reset analysis data\n\n const { onAudioStream, ...options } = recordingOptions;\n const { enableProcessing } = options;\n\n const maxRecentDataDuration = 10000; // TODO compute maxRecentDataDuration based on screen dimensions\n if (typeof onAudioStream === \"function\") {\n onAudioStreamRef.current = onAudioStream;\n } else {\n console.warn(`${TAG} onAudioStream is not a function`, onAudioStream);\n onAudioStreamRef.current = null;\n }\n const startResult: StartRecordingResult =\n await ExpoAudioStream.startRecording(options);\n dispatch({ type: \"START\" });\n\n if (enableProcessing) {\n logger.debug(`Enabling audio analysis listener`);\n const listener = addAudioAnalysisListener(async (analysisData) => {\n try {\n await handleAudioAnalysis({\n analysis: analysisData,\n visualizationDuration: maxRecentDataDuration,\n });\n } catch (error) {\n console.warn(`${TAG} Error processing audio analysis:`, error);\n }\n });\n\n analysisListenerRef.current = listener;\n }\n\n return startResult;\n },\n [handleAudioAnalysis, dispatch],\n );\n\n const stopRecording = useCallback(async () => {\n logger.debug(`stoping recording`);\n\n if (analysisListenerRef.current) {\n analysisListenerRef.current.remove();\n analysisListenerRef.current = null;\n }\n\n const stopResult: AudioRecordingResult =\n await ExpoAudioStream.stopRecording();\n onAudioStreamRef.current = null;\n logger.debug(`recording stopped`, stopResult);\n dispatch({ type: \"STOP\" });\n return stopResult;\n }, [dispatch]);\n\n const pauseRecording = useCallback(async () => {\n logger.debug(`pause recording`);\n const pauseResult = await ExpoAudioStream.pauseRecording();\n dispatch({ type: \"PAUSE\" });\n return pauseResult;\n }, [dispatch]);\n\n const resumeRecording = useCallback(async () => {\n logger.debug(`resume recording`);\n const resumeResult = await ExpoAudioStream.resumeRecording();\n dispatch({ type: \"RESUME\" });\n return resumeResult;\n }, [dispatch]);\n\n useEffect(() => {\n let interval: ReturnType<typeof setTimeout>;\n if (state.isRecording) {\n interval = setInterval(checkStatus, 1000);\n }\n return () => {\n if (interval) {\n clearInterval(interval);\n }\n };\n }, [checkStatus, state.isRecording]);\n\n useEffect(() => {\n logger.debug(`Registering audio event listener`);\n const subscribeAudio = addAudioEventListener(handleAudioEvent);\n\n logger.debug(`Subscribed to audio event listener and analysis listener`, {\n subscribeAudio,\n });\n\n return () => {\n logger.debug(`Removing audio event listener`);\n subscribeAudio.remove();\n };\n }, [handleAudioEvent, handleAudioAnalysis]);\n\n useEffect(() => {\n if (debug) {\n enableAllLoggers();\n } else {\n disableAllLoggers();\n }\n }, [debug]);\n\n return {\n startRecording,\n stopRecording,\n pauseRecording,\n resumeRecording,\n isPaused: state.isPaused,\n isRecording: state.isRecording,\n durationMs: state.durationMs,\n size: state.size,\n analysisData: state.analysisData,\n };\n}\n"]}
@@ -1,11 +0,0 @@
1
- export declare const WAV_HEADER_SIZE = 44;
2
- export declare const convertPCMToFloat32: ({ bitDepth, buffer, skipWavHeader, }: {
3
- buffer: ArrayBuffer;
4
- bitDepth: number;
5
- skipWavHeader?: boolean;
6
- }) => {
7
- pcmValues: Float32Array;
8
- min: number;
9
- max: number;
10
- };
11
- //# sourceMappingURL=convertPCMToFloat32.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"convertPCMToFloat32.d.ts","sourceRoot":"","sources":["../../src/utils/convertPCMToFloat32.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,mBAAmB,yCAI7B;IACD,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,KAAG;IAAE,SAAS,EAAE,YAAY,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAsCtD,CAAC"}
@@ -1,41 +0,0 @@
1
- export const WAV_HEADER_SIZE = 44;
2
- export const convertPCMToFloat32 = ({ bitDepth, buffer, skipWavHeader = false, }) => {
3
- const dataView = new DataView(buffer);
4
- const headerOffset = skipWavHeader ? WAV_HEADER_SIZE : 0;
5
- const dataLength = buffer.byteLength - headerOffset;
6
- const sampleLength = dataLength / (bitDepth / 8);
7
- const float32Array = new Float32Array(sampleLength);
8
- let min = Infinity;
9
- let max = -Infinity;
10
- for (let i = 0; i < sampleLength; i++) {
11
- let value = 0;
12
- const offset = headerOffset + i * (bitDepth / 8);
13
- switch (bitDepth) {
14
- case 8:
15
- value = dataView.getUint8(offset) / 128;
16
- break;
17
- case 16:
18
- value = dataView.getInt16(offset, true) / 32768;
19
- break;
20
- case 24:
21
- value =
22
- (dataView.getUint8(offset) +
23
- (dataView.getUint8(offset + 1) << 8) +
24
- (dataView.getUint8(offset + 2) << 16)) /
25
- 8388608;
26
- break;
27
- case 32:
28
- value = dataView.getFloat32(offset, true);
29
- break;
30
- default:
31
- throw new Error(`Unsupported bit depth: ${bitDepth}`);
32
- }
33
- if (value < min)
34
- min = value;
35
- if (value > max)
36
- max = value;
37
- float32Array[i] = value;
38
- }
39
- return { pcmValues: float32Array, min, max };
40
- };
41
- //# sourceMappingURL=convertPCMToFloat32.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"convertPCMToFloat32.js","sourceRoot":"","sources":["../../src/utils/convertPCMToFloat32.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAClC,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,aAAa,GAAG,KAAK,GAKtB,EAAyD,EAAE;IAC1D,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,YAAY,GAAG,aAAa,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,GAAG,YAAY,CAAC;IACpD,MAAM,YAAY,GAAG,UAAU,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;IACjD,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,YAAY,CAAC,CAAC;IACpD,IAAI,GAAG,GAAG,QAAQ,CAAC;IACnB,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC;IAEpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,MAAM,MAAM,GAAG,YAAY,GAAG,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;QACjD,QAAQ,QAAQ,EAAE,CAAC;YACjB,KAAK,CAAC;gBACJ,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC;gBACxC,MAAM;YACR,KAAK,EAAE;gBACL,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC;gBAChD,MAAM;YACR,KAAK,EAAE;gBACL,KAAK;oBACH,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;wBACxB,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;wBACpC,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;wBACxC,OAAO,CAAC;gBACV,MAAM;YACR,KAAK,EAAE;gBACL,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBAC1C,MAAM;YACR;gBACE,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,KAAK,GAAG,GAAG;YAAE,GAAG,GAAG,KAAK,CAAC;QAC7B,IAAI,KAAK,GAAG,GAAG;YAAE,GAAG,GAAG,KAAK,CAAC;QAC7B,YAAY,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;IAC1B,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAC/C,CAAC,CAAC","sourcesContent":["export const WAV_HEADER_SIZE = 44;\nexport const convertPCMToFloat32 = ({\n bitDepth,\n buffer,\n skipWavHeader = false,\n}: {\n buffer: ArrayBuffer;\n bitDepth: number;\n skipWavHeader?: boolean;\n}): { pcmValues: Float32Array; min: number; max: number } => {\n const dataView = new DataView(buffer);\n const headerOffset = skipWavHeader ? WAV_HEADER_SIZE : 0;\n const dataLength = buffer.byteLength - headerOffset;\n const sampleLength = dataLength / (bitDepth / 8);\n const float32Array = new Float32Array(sampleLength);\n let min = Infinity;\n let max = -Infinity;\n\n for (let i = 0; i < sampleLength; i++) {\n let value = 0;\n const offset = headerOffset + i * (bitDepth / 8);\n switch (bitDepth) {\n case 8:\n value = dataView.getUint8(offset) / 128;\n break;\n case 16:\n value = dataView.getInt16(offset, true) / 32768;\n break;\n case 24:\n value =\n (dataView.getUint8(offset) +\n (dataView.getUint8(offset + 1) << 8) +\n (dataView.getUint8(offset + 2) << 16)) /\n 8388608;\n break;\n case 32:\n value = dataView.getFloat32(offset, true);\n break;\n default:\n throw new Error(`Unsupported bit depth: ${bitDepth}`);\n }\n if (value < min) min = value;\n if (value > max) max = value;\n float32Array[i] = value;\n }\n\n return { pcmValues: float32Array, min, max };\n};\n"]}
@@ -1,5 +0,0 @@
1
- import { BitDepth, EncodingType } from "../ExpoAudioStream.types";
2
- export declare const encodingToBitDepth: ({ encoding, }: {
3
- encoding: EncodingType;
4
- }) => BitDepth;
5
- //# sourceMappingURL=encodingToBitDepth.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"encodingToBitDepth.d.ts","sourceRoot":"","sources":["../../src/utils/encodingToBitDepth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAElE,eAAO,MAAM,kBAAkB,kBAE5B;IACD,QAAQ,EAAE,YAAY,CAAC;CACxB,KAAG,QAWH,CAAC"}
@@ -1,13 +0,0 @@
1
- export const encodingToBitDepth = ({ encoding, }) => {
2
- switch (encoding) {
3
- case "pcm_32bit":
4
- return 32;
5
- case "pcm_16bit":
6
- return 16;
7
- case "pcm_8bit":
8
- return 8;
9
- default:
10
- throw new Error(`Unsupported encoding type: ${encoding}`);
11
- }
12
- };
13
- //# sourceMappingURL=encodingToBitDepth.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"encodingToBitDepth.js","sourceRoot":"","sources":["../../src/utils/encodingToBitDepth.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,EACjC,QAAQ,GAGT,EAAY,EAAE;IACb,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,WAAW;YACd,OAAO,EAAE,CAAC;QACZ,KAAK,WAAW;YACd,OAAO,EAAE,CAAC;QACZ,KAAK,UAAU;YACb,OAAO,CAAC,CAAC;QACX;YACE,MAAM,IAAI,KAAK,CAAC,8BAA8B,QAAQ,EAAE,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC,CAAC","sourcesContent":["import { BitDepth, EncodingType } from \"../ExpoAudioStream.types\";\n\nexport const encodingToBitDepth = ({\n encoding,\n}: {\n encoding: EncodingType;\n}): BitDepth => {\n switch (encoding) {\n case \"pcm_32bit\":\n return 32;\n case \"pcm_16bit\":\n return 16;\n case \"pcm_8bit\":\n return 8;\n default:\n throw new Error(`Unsupported encoding type: ${encoding}`);\n }\n};\n"]}
@@ -1,25 +0,0 @@
1
- import { BitDepth, SampleRate } from "../ExpoAudioStream.types";
2
- /**
3
- * Interface representing the metadata of a WAV file.
4
- */
5
- export interface WavFileInfo {
6
- sampleRate: SampleRate;
7
- numChannels: number;
8
- bitDepth: BitDepth;
9
- size: number;
10
- durationMs: number;
11
- audioFormatDescription: string;
12
- byteRate: number;
13
- blockAlign: number;
14
- creationDateTime?: string;
15
- comments?: string;
16
- compressionType?: string;
17
- }
18
- /**
19
- * Extracts metadata from a WAV file.
20
- *
21
- * @param arrayBuffer - The array buffer containing the WAV file data.
22
- * @returns A promise that resolves to the extracted metadata.
23
- */
24
- export declare const getWavFileInfo: (arrayBuffer: ArrayBuffer) => Promise<WavFileInfo>;
25
- //# sourceMappingURL=getWavFileInfo.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"getWavFileInfo.d.ts","sourceRoot":"","sources":["../../src/utils/getWavFileInfo.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAoBhE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,gBACZ,WAAW,KACvB,OAAO,CAAC,WAAW,CA6ErB,CAAC"}
@@ -1,89 +0,0 @@
1
- // packages/expo-audio-stream/src/utils/getWavFileInfo.ts
2
- import { DATA_CHUNK_ID, DEFAULT_BIT_DEPTH, DEFAULT_SAMPLE_RATE, FMT_CHUNK_ID, INFO_CHUNK_ID, RIFF_HEADER, WAVE_HEADER, } from "../constants";
3
- // Audio format descriptions
4
- const AUDIO_FORMATS = {
5
- 1: "PCM",
6
- 3: "IEEE float",
7
- 6: "8-bit ITU-T G.711 A-law",
8
- 7: "8-bit ITU-T G.711 µ-law",
9
- 65534: "WAVE_FORMAT_EXTENSIBLE",
10
- };
11
- /**
12
- * Extracts metadata from a WAV file.
13
- *
14
- * @param arrayBuffer - The array buffer containing the WAV file data.
15
- * @returns A promise that resolves to the extracted metadata.
16
- */
17
- export const getWavFileInfo = async (arrayBuffer) => {
18
- const view = new DataView(arrayBuffer);
19
- // Check if the file is a valid RIFF/WAVE file
20
- const riffHeader = view.getUint32(0, false);
21
- const waveHeader = view.getUint32(8, false);
22
- if (riffHeader !== RIFF_HEADER || waveHeader !== WAVE_HEADER) {
23
- throw new Error("Invalid WAV file");
24
- }
25
- // Initialize variables for the metadata
26
- let fmtChunkOffset = 12;
27
- let sampleRate = DEFAULT_SAMPLE_RATE;
28
- let numChannels = 0;
29
- let bitDepth = DEFAULT_BIT_DEPTH;
30
- let dataChunkSize = 0;
31
- let audioFormat = 0;
32
- let byteRate = 0;
33
- let blockAlign = 0;
34
- let creationDateTime = "";
35
- let comments = "";
36
- // Parse chunks to find the "fmt " and "data" chunks
37
- while (fmtChunkOffset < view.byteLength) {
38
- const chunkId = view.getUint32(fmtChunkOffset, false);
39
- const chunkSize = view.getUint32(fmtChunkOffset + 4, true);
40
- if (chunkId === FMT_CHUNK_ID) {
41
- // "fmt "
42
- audioFormat = view.getUint16(fmtChunkOffset + 8, true);
43
- if (!AUDIO_FORMATS[audioFormat]) {
44
- throw new Error("Unsupported WAV file format");
45
- }
46
- numChannels = view.getUint16(fmtChunkOffset + 10, true);
47
- sampleRate = view.getUint32(fmtChunkOffset + 12, true);
48
- byteRate = view.getUint32(fmtChunkOffset + 16, true);
49
- blockAlign = view.getUint16(fmtChunkOffset + 20, true);
50
- bitDepth = view.getUint16(fmtChunkOffset + 22, true);
51
- }
52
- else if (chunkId === DATA_CHUNK_ID) {
53
- // "data"
54
- dataChunkSize = chunkSize;
55
- break;
56
- }
57
- else if (chunkId === INFO_CHUNK_ID) {
58
- // "INFO"
59
- // Read INFO chunk (assuming it contains a text-based creation date/time and comments)
60
- const infoStart = fmtChunkOffset + 8;
61
- const infoText = new TextDecoder().decode(new Uint8Array(arrayBuffer.slice(infoStart, infoStart + chunkSize)));
62
- const infoParts = infoText.split("\0");
63
- creationDateTime = infoParts[0];
64
- comments = infoParts[1];
65
- }
66
- fmtChunkOffset += 8 + chunkSize;
67
- }
68
- if (!sampleRate || !numChannels || !bitDepth || !dataChunkSize) {
69
- throw new Error("Incomplete WAV file information");
70
- }
71
- // Calculate duration
72
- const bytesPerSample = bitDepth / 8;
73
- const numSamples = dataChunkSize / (numChannels * bytesPerSample);
74
- const durationMs = (numSamples / sampleRate) * 1000;
75
- return {
76
- sampleRate,
77
- numChannels,
78
- bitDepth,
79
- size: arrayBuffer.byteLength,
80
- durationMs,
81
- audioFormatDescription: AUDIO_FORMATS[audioFormat],
82
- byteRate,
83
- blockAlign,
84
- creationDateTime: creationDateTime || undefined,
85
- comments: comments || undefined,
86
- compressionType: audioFormat === 1 ? "None" : AUDIO_FORMATS[audioFormat],
87
- };
88
- };
89
- //# sourceMappingURL=getWavFileInfo.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"getWavFileInfo.js","sourceRoot":"","sources":["../../src/utils/getWavFileInfo.ts"],"names":[],"mappings":"AAAA,yDAAyD;AAGzD,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,WAAW,EACX,WAAW,GACZ,MAAM,cAAc,CAAC;AAEtB,4BAA4B;AAC5B,MAAM,aAAa,GAA8B;IAC/C,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,YAAY;IACf,CAAC,EAAE,yBAAyB;IAC5B,CAAC,EAAE,yBAAyB;IAC5B,KAAK,EAAE,wBAAwB;CAChC,CAAC;AAmBF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EACjC,WAAwB,EACF,EAAE;IACxB,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,WAAW,CAAC,CAAC;IAEvC,8CAA8C;IAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC5C,IAAI,UAAU,KAAK,WAAW,IAAI,UAAU,KAAK,WAAW,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;IAED,wCAAwC;IACxC,IAAI,cAAc,GAAG,EAAE,CAAC;IACxB,IAAI,UAAU,GAAe,mBAAmB,CAAC;IACjD,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,QAAQ,GAAa,iBAAiB,CAAC;IAC3C,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,gBAAgB,GAAG,EAAE,CAAC;IAC1B,IAAI,QAAQ,GAAG,EAAE,CAAC;IAElB,oDAAoD;IACpD,OAAO,cAAc,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;QACtD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;QAC3D,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;YAC7B,SAAS;YACT,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;YACvD,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC;gBAChC,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;YACjD,CAAC;YACD,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;YACxD,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,GAAG,EAAE,EAAE,IAAI,CAAe,CAAC;YACrE,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;YACrD,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;YACvD,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,GAAG,EAAE,EAAE,IAAI,CAAa,CAAC;QACnE,CAAC;aAAM,IAAI,OAAO,KAAK,aAAa,EAAE,CAAC;YACrC,SAAS;YACT,aAAa,GAAG,SAAS,CAAC;YAC1B,MAAM;QACR,CAAC;aAAM,IAAI,OAAO,KAAK,aAAa,EAAE,CAAC;YACrC,SAAS;YACT,sFAAsF;YACtF,MAAM,SAAS,GAAG,cAAc,GAAG,CAAC,CAAC;YACrC,MAAM,QAAQ,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CACvC,IAAI,UAAU,CAAC,WAAW,CAAC,KAAK,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAAC,CAAC,CACpE,CAAC;YACF,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACvC,gBAAgB,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;YAChC,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QAC1B,CAAC;QACD,cAAc,IAAI,CAAC,GAAG,SAAS,CAAC;IAClC,CAAC;IAED,IAAI,CAAC,UAAU,IAAI,CAAC,WAAW,IAAI,CAAC,QAAQ,IAAI,CAAC,aAAa,EAAE,CAAC;QAC/D,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IAED,qBAAqB;IACrB,MAAM,cAAc,GAAG,QAAQ,GAAG,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,aAAa,GAAG,CAAC,WAAW,GAAG,cAAc,CAAC,CAAC;IAClE,MAAM,UAAU,GAAG,CAAC,UAAU,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC;IAEpD,OAAO;QACL,UAAU;QACV,WAAW;QACX,QAAQ;QACR,IAAI,EAAE,WAAW,CAAC,UAAU;QAC5B,UAAU;QACV,sBAAsB,EAAE,aAAa,CAAC,WAAW,CAAC;QAClD,QAAQ;QACR,UAAU;QACV,gBAAgB,EAAE,gBAAgB,IAAI,SAAS;QAC/C,QAAQ,EAAE,QAAQ,IAAI,SAAS;QAC/B,eAAe,EAAE,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,WAAW,CAAC;KACzE,CAAC;AACJ,CAAC,CAAC","sourcesContent":["// packages/expo-audio-stream/src/utils/getWavFileInfo.ts\n\nimport { BitDepth, SampleRate } from \"../ExpoAudioStream.types\";\nimport {\n DATA_CHUNK_ID,\n DEFAULT_BIT_DEPTH,\n DEFAULT_SAMPLE_RATE,\n FMT_CHUNK_ID,\n INFO_CHUNK_ID,\n RIFF_HEADER,\n WAVE_HEADER,\n} from \"../constants\";\n\n// Audio format descriptions\nconst AUDIO_FORMATS: { [key: number]: string } = {\n 1: \"PCM\",\n 3: \"IEEE float\",\n 6: \"8-bit ITU-T G.711 A-law\",\n 7: \"8-bit ITU-T G.711 µ-law\",\n 65534: \"WAVE_FORMAT_EXTENSIBLE\",\n};\n\n/**\n * Interface representing the metadata of a WAV file.\n */\nexport interface WavFileInfo {\n sampleRate: SampleRate;\n numChannels: number;\n bitDepth: BitDepth;\n size: number; // in bytes\n durationMs: number; // in ms\n audioFormatDescription: string; // Description of the audio format\n byteRate: number; // Average bytes per second\n blockAlign: number; // Number of bytes for one sample including all channels\n creationDateTime?: string; // Optional creation date and time\n comments?: string; // Optional comments or tags\n compressionType?: string; // Optional compression type\n}\n\n/**\n * Extracts metadata from a WAV file.\n *\n * @param arrayBuffer - The array buffer containing the WAV file data.\n * @returns A promise that resolves to the extracted metadata.\n */\nexport const getWavFileInfo = async (\n arrayBuffer: ArrayBuffer,\n): Promise<WavFileInfo> => {\n const view = new DataView(arrayBuffer);\n\n // Check if the file is a valid RIFF/WAVE file\n const riffHeader = view.getUint32(0, false);\n const waveHeader = view.getUint32(8, false);\n if (riffHeader !== RIFF_HEADER || waveHeader !== WAVE_HEADER) {\n throw new Error(\"Invalid WAV file\");\n }\n\n // Initialize variables for the metadata\n let fmtChunkOffset = 12;\n let sampleRate: SampleRate = DEFAULT_SAMPLE_RATE;\n let numChannels = 0;\n let bitDepth: BitDepth = DEFAULT_BIT_DEPTH;\n let dataChunkSize = 0;\n let audioFormat = 0;\n let byteRate = 0;\n let blockAlign = 0;\n let creationDateTime = \"\";\n let comments = \"\";\n\n // Parse chunks to find the \"fmt \" and \"data\" chunks\n while (fmtChunkOffset < view.byteLength) {\n const chunkId = view.getUint32(fmtChunkOffset, false);\n const chunkSize = view.getUint32(fmtChunkOffset + 4, true);\n if (chunkId === FMT_CHUNK_ID) {\n // \"fmt \"\n audioFormat = view.getUint16(fmtChunkOffset + 8, true);\n if (!AUDIO_FORMATS[audioFormat]) {\n throw new Error(\"Unsupported WAV file format\");\n }\n numChannels = view.getUint16(fmtChunkOffset + 10, true);\n sampleRate = view.getUint32(fmtChunkOffset + 12, true) as SampleRate;\n byteRate = view.getUint32(fmtChunkOffset + 16, true);\n blockAlign = view.getUint16(fmtChunkOffset + 20, true);\n bitDepth = view.getUint16(fmtChunkOffset + 22, true) as BitDepth;\n } else if (chunkId === DATA_CHUNK_ID) {\n // \"data\"\n dataChunkSize = chunkSize;\n break;\n } else if (chunkId === INFO_CHUNK_ID) {\n // \"INFO\"\n // Read INFO chunk (assuming it contains a text-based creation date/time and comments)\n const infoStart = fmtChunkOffset + 8;\n const infoText = new TextDecoder().decode(\n new Uint8Array(arrayBuffer.slice(infoStart, infoStart + chunkSize)),\n );\n const infoParts = infoText.split(\"\\0\");\n creationDateTime = infoParts[0];\n comments = infoParts[1];\n }\n fmtChunkOffset += 8 + chunkSize;\n }\n\n if (!sampleRate || !numChannels || !bitDepth || !dataChunkSize) {\n throw new Error(\"Incomplete WAV file information\");\n }\n\n // Calculate duration\n const bytesPerSample = bitDepth / 8;\n const numSamples = dataChunkSize / (numChannels * bytesPerSample);\n const durationMs = (numSamples / sampleRate) * 1000;\n\n return {\n sampleRate,\n numChannels,\n bitDepth,\n size: arrayBuffer.byteLength,\n durationMs,\n audioFormatDescription: AUDIO_FORMATS[audioFormat],\n byteRate,\n blockAlign,\n creationDateTime: creationDateTime || undefined,\n comments: comments || undefined,\n compressionType: audioFormat === 1 ? \"None\" : AUDIO_FORMATS[audioFormat],\n };\n};\n"]}
@@ -1,9 +0,0 @@
1
- interface WavHeaderOptions {
2
- buffer: ArrayBuffer;
3
- sampleRate: number;
4
- numChannels: number;
5
- bitDepth: number;
6
- }
7
- export declare const writeWavHeader: ({ buffer, sampleRate, numChannels, bitDepth, }: WavHeaderOptions) => ArrayBuffer;
8
- export {};
9
- //# sourceMappingURL=writeWavHeader.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"writeWavHeader.d.ts","sourceRoot":"","sources":["../../src/utils/writeWavHeader.ts"],"names":[],"mappings":"AACA,UAAU,gBAAgB;IACxB,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,cAAc,mDAKxB,gBAAgB,KAAG,WA0CrB,CAAC"}
@@ -1,41 +0,0 @@
1
- export const writeWavHeader = ({ buffer, sampleRate, numChannels, bitDepth, }) => {
2
- const bytesPerSample = bitDepth / 8;
3
- const numSamples = buffer.byteLength / (numChannels * bytesPerSample);
4
- const view = new DataView(buffer);
5
- const blockAlign = numChannels * bytesPerSample;
6
- const byteRate = sampleRate * blockAlign;
7
- // Function to write a string to the DataView
8
- const writeString = (view, offset, string) => {
9
- for (let i = 0; i < string.length; i++) {
10
- view.setUint8(offset + i, string.charCodeAt(i));
11
- }
12
- };
13
- // Check if the buffer already has a WAV header by looking for "RIFF" at the start
14
- const existingHeader = view.getUint32(0, false) === 0x52494646; // "RIFF" in ASCII
15
- if (!existingHeader) {
16
- // Write the WAV header
17
- writeString(view, 0, "RIFF"); // ChunkID
18
- view.setUint32(4, 36 + numSamples * blockAlign, true); // ChunkSize
19
- writeString(view, 8, "WAVE"); // Format
20
- writeString(view, 12, "fmt "); // Subchunk1ID
21
- view.setUint32(16, 16, true); // Subchunk1Size (16 for PCM)
22
- view.setUint16(20, bitDepth === 32 ? 3 : 1, true); // AudioFormat (3 for float, 1 for PCM)
23
- view.setUint16(22, numChannels, true); // NumChannels
24
- view.setUint32(24, sampleRate, true); // SampleRate
25
- view.setUint32(28, byteRate, true); // ByteRate
26
- view.setUint16(32, blockAlign, true); // BlockAlign
27
- view.setUint16(34, bitDepth, true); // BitsPerSample
28
- writeString(view, 36, "data"); // Subchunk2ID
29
- view.setUint32(40, numSamples * blockAlign, true); // Subchunk2Size
30
- }
31
- else {
32
- // Update the existing WAV header if necessary
33
- view.setUint32(4, 36 + numSamples * blockAlign, true); // Update ChunkSize
34
- view.setUint32(24, sampleRate, true); // Update SampleRate
35
- view.setUint32(28, byteRate, true); // Update ByteRate
36
- view.setUint16(32, blockAlign, true); // Update BlockAlign
37
- view.setUint32(40, numSamples * blockAlign, true); // Update Subchunk2Size
38
- }
39
- return buffer;
40
- };
41
- //# sourceMappingURL=writeWavHeader.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"writeWavHeader.js","sourceRoot":"","sources":["../../src/utils/writeWavHeader.ts"],"names":[],"mappings":"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","sourcesContent":["// packages/expo-audio-stream/src/utils/writeWavHeader.ts\ninterface WavHeaderOptions {\n buffer: ArrayBuffer;\n sampleRate: number;\n numChannels: number;\n bitDepth: number;\n}\n\nexport const writeWavHeader = ({\n buffer,\n sampleRate,\n numChannels,\n bitDepth,\n}: WavHeaderOptions): ArrayBuffer => {\n const bytesPerSample = bitDepth / 8;\n const numSamples = buffer.byteLength / (numChannels * bytesPerSample);\n const view = new DataView(buffer);\n const blockAlign = numChannels * bytesPerSample;\n const byteRate = sampleRate * blockAlign;\n\n // Function to write a string to the DataView\n const writeString = (view: DataView, offset: number, string: string) => {\n for (let i = 0; i < string.length; i++) {\n view.setUint8(offset + i, string.charCodeAt(i));\n }\n };\n\n // Check if the buffer already has a WAV header by looking for \"RIFF\" at the start\n const existingHeader = view.getUint32(0, false) === 0x52494646; // \"RIFF\" in ASCII\n\n if (!existingHeader) {\n // Write the WAV header\n writeString(view, 0, \"RIFF\"); // ChunkID\n view.setUint32(4, 36 + numSamples * blockAlign, true); // ChunkSize\n writeString(view, 8, \"WAVE\"); // Format\n writeString(view, 12, \"fmt \"); // Subchunk1ID\n view.setUint32(16, 16, true); // Subchunk1Size (16 for PCM)\n view.setUint16(20, bitDepth === 32 ? 3 : 1, true); // AudioFormat (3 for float, 1 for PCM)\n view.setUint16(22, numChannels, true); // NumChannels\n view.setUint32(24, sampleRate, true); // SampleRate\n view.setUint32(28, byteRate, true); // ByteRate\n view.setUint16(32, blockAlign, true); // BlockAlign\n view.setUint16(34, bitDepth, true); // BitsPerSample\n writeString(view, 36, \"data\"); // Subchunk2ID\n view.setUint32(40, numSamples * blockAlign, true); // Subchunk2Size\n } else {\n // Update the existing WAV header if necessary\n view.setUint32(4, 36 + numSamples * blockAlign, true); // Update ChunkSize\n view.setUint32(24, sampleRate, true); // Update SampleRate\n view.setUint32(28, byteRate, true); // Update ByteRate\n view.setUint16(32, blockAlign, true); // Update BlockAlign\n view.setUint32(40, numSamples * blockAlign, true); // Update Subchunk2Size\n }\n\n return buffer;\n};\n"]}