@siteed/expo-audio-stream 1.0.2 → 1.0.3

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 (113) hide show
  1. package/.size-limit.json +6 -0
  2. package/build/AudioAnalysis/AudioAnalysis.types.d.ts +76 -0
  3. package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -0
  4. package/build/AudioAnalysis/AudioAnalysis.types.js +3 -0
  5. package/build/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
  6. package/build/AudioAnalysis/extractAudioAnalysis.d.ts +4 -0
  7. package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -0
  8. package/build/AudioAnalysis/extractAudioAnalysis.js +101 -0
  9. package/build/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
  10. package/build/AudioAnalysis/extractWaveform.d.ts +8 -0
  11. package/build/AudioAnalysis/extractWaveform.d.ts.map +1 -0
  12. package/build/AudioAnalysis/extractWaveform.js +14 -0
  13. package/build/AudioAnalysis/extractWaveform.js.map +1 -0
  14. package/build/AudioRecorder.provider.d.ts +14 -1
  15. package/build/AudioRecorder.provider.d.ts.map +1 -1
  16. package/build/AudioRecorder.provider.js +17 -4
  17. package/build/AudioRecorder.provider.js.map +1 -1
  18. package/build/ExpoAudioStream.types.d.ts +26 -84
  19. package/build/ExpoAudioStream.types.d.ts.map +1 -1
  20. package/build/ExpoAudioStream.types.js.map +1 -1
  21. package/build/ExpoAudioStream.web.d.ts +6 -5
  22. package/build/ExpoAudioStream.web.d.ts.map +1 -1
  23. package/build/ExpoAudioStream.web.js +9 -8
  24. package/build/ExpoAudioStream.web.js.map +1 -1
  25. package/build/ExpoAudioStreamModule.d.ts.map +1 -1
  26. package/build/ExpoAudioStreamModule.js +5 -1
  27. package/build/ExpoAudioStreamModule.js.map +1 -1
  28. package/build/{WebRecorder.d.ts → WebRecorder.web.d.ts} +7 -3
  29. package/build/WebRecorder.web.d.ts.map +1 -0
  30. package/build/{WebRecorder.js → WebRecorder.web.js} +74 -29
  31. package/build/WebRecorder.web.js.map +1 -0
  32. package/build/constants.d.ts +11 -0
  33. package/build/constants.d.ts.map +1 -0
  34. package/build/constants.js +14 -0
  35. package/build/constants.js.map +1 -0
  36. package/build/events.d.ts +6 -0
  37. package/build/events.d.ts.map +1 -0
  38. package/build/events.js +15 -0
  39. package/build/events.js.map +1 -0
  40. package/build/index.d.ts +8 -16
  41. package/build/index.d.ts.map +1 -1
  42. package/build/index.js +6 -112
  43. package/build/index.js.map +1 -1
  44. package/build/logger.d.ts +9 -0
  45. package/build/logger.d.ts.map +1 -0
  46. package/build/logger.js +17 -0
  47. package/build/logger.js.map +1 -0
  48. package/build/{useAudioRecording.d.ts → useAudioRecorder.d.ts} +6 -7
  49. package/build/useAudioRecorder.d.ts.map +1 -0
  50. package/build/{useAudioRecording.js → useAudioRecorder.js} +69 -65
  51. package/build/useAudioRecorder.js.map +1 -0
  52. package/build/utils/convertPCMToFloat32.d.ts +11 -0
  53. package/build/utils/convertPCMToFloat32.d.ts.map +1 -0
  54. package/build/utils/convertPCMToFloat32.js +41 -0
  55. package/build/utils/convertPCMToFloat32.js.map +1 -0
  56. package/build/utils/encodingToBitDepth.d.ts +5 -0
  57. package/build/utils/encodingToBitDepth.d.ts.map +1 -0
  58. package/build/utils/encodingToBitDepth.js +13 -0
  59. package/build/utils/encodingToBitDepth.js.map +1 -0
  60. package/build/utils/getWavFileInfo.d.ts +25 -0
  61. package/build/utils/getWavFileInfo.d.ts.map +1 -0
  62. package/build/utils/getWavFileInfo.js +89 -0
  63. package/build/utils/getWavFileInfo.js.map +1 -0
  64. package/build/utils/writeWavHeader.d.ts +9 -0
  65. package/build/utils/writeWavHeader.d.ts.map +1 -0
  66. package/build/utils/writeWavHeader.js +41 -0
  67. package/build/utils/writeWavHeader.js.map +1 -0
  68. package/build/workers/InlineFeaturesExtractor.web.d.ts +2 -0
  69. package/build/workers/InlineFeaturesExtractor.web.d.ts.map +1 -0
  70. package/build/workers/InlineFeaturesExtractor.web.js +303 -0
  71. package/build/workers/InlineFeaturesExtractor.web.js.map +1 -0
  72. package/build/workers/inlineAudioWebWorker.web.d.ts +2 -0
  73. package/build/workers/inlineAudioWebWorker.web.d.ts.map +1 -0
  74. package/build/workers/inlineAudioWebWorker.web.js +243 -0
  75. package/build/workers/inlineAudioWebWorker.web.js.map +1 -0
  76. package/ios/AudioStreamManager.swift +39 -2
  77. package/ios/ExpoAudioStreamModule.swift +10 -0
  78. package/package.json +7 -6
  79. package/plugin/tsconfig.json +1 -1
  80. package/publish.sh +0 -0
  81. package/src/AudioAnalysis/AudioAnalysis.types.ts +85 -0
  82. package/src/AudioAnalysis/extractAudioAnalysis.ts +136 -0
  83. package/src/AudioAnalysis/extractWaveform.ts +25 -0
  84. package/src/AudioRecorder.provider.tsx +35 -7
  85. package/src/ExpoAudioStream.types.ts +33 -94
  86. package/src/ExpoAudioStream.web.ts +17 -16
  87. package/src/ExpoAudioStreamModule.ts +6 -1
  88. package/src/{WebRecorder.ts → WebRecorder.web.ts} +85 -33
  89. package/src/constants.ts +18 -0
  90. package/src/events.ts +25 -0
  91. package/src/index.ts +8 -169
  92. package/src/logger.ts +26 -0
  93. package/src/{useAudioRecording.tsx → useAudioRecorder.tsx} +141 -136
  94. package/src/utils/convertPCMToFloat32.ts +48 -0
  95. package/src/utils/encodingToBitDepth.ts +18 -0
  96. package/src/utils/getWavFileInfo.ts +125 -0
  97. package/src/utils/writeWavHeader.ts +56 -0
  98. package/src/workers/InlineFeaturesExtractor.web.tsx +302 -0
  99. package/src/workers/inlineAudioWebWorker.web.tsx +242 -0
  100. package/build/WebRecorder.d.ts.map +0 -1
  101. package/build/WebRecorder.js.map +0 -1
  102. package/build/inlineAudioWebWorker.d.ts +0 -3
  103. package/build/inlineAudioWebWorker.d.ts.map +0 -1
  104. package/build/inlineAudioWebWorker.js +0 -340
  105. package/build/inlineAudioWebWorker.js.map +0 -1
  106. package/build/useAudioRecording.d.ts.map +0 -1
  107. package/build/useAudioRecording.js.map +0 -1
  108. package/build/utils.d.ts +0 -31
  109. package/build/utils.d.ts.map +0 -1
  110. package/build/utils.js +0 -143
  111. package/build/utils.js.map +0 -1
  112. package/src/inlineAudioWebWorker.tsx +0 -340
  113. package/src/utils.ts +0 -189
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// src/index.ts\nimport { EventEmitter, type Subscription } from \"expo-modules-core\";\nimport { Platform } from \"react-native\";\n\n// Import the native module. On web, it will be resolved to ExpoAudioStream.web.ts\n// and on native platforms to ExpoAudioStream.ts\nimport {\n AudioRecorderProvider,\n useSharedAudioRecorder,\n} from \"./AudioRecorder.provider\";\nimport { AudioAnalysisData, AudioEventPayload } from \"./ExpoAudioStream.types\";\nimport ExpoAudioStreamModule from \"./ExpoAudioStreamModule\";\nimport { ExtractMetadataProps, useAudioRecorder } from \"./useAudioRecording\";\nimport { convertPCMToFloat32, getWavFileInfo, writeWavHeader } from \"./utils\";\n\nconst emitter = new EventEmitter(ExpoAudioStreamModule);\n\nexport function addAudioEventListener(\n listener: (event: AudioEventPayload) => Promise<void>,\n): Subscription {\n console.log(`addAudioEventListener`, listener);\n return emitter.addListener<AudioEventPayload>(\"AudioData\", listener);\n}\n\nexport function addAudioAnalysisListener(\n listener: (event: AudioAnalysisData) => Promise<void>,\n): Subscription {\n console.log(`addAudioAnalysisListener`, listener);\n return emitter.addListener<AudioAnalysisData>(\"AudioAnalysis\", listener);\n}\n\nconst isWeb = Platform.OS === \"web\";\n\nexport const extractAudioAnalysis = async ({\n fileUri,\n pointsPerSecond = 20,\n arrayBuffer,\n bitDepth,\n skipWavHeader,\n durationMs,\n sampleRate,\n numberOfChannels,\n algorithm = \"rms\",\n features,\n featuresExtratorUrl = \"/audio-features-extractor.js\",\n}: ExtractMetadataProps): Promise<AudioAnalysisData> => {\n if (isWeb) {\n if (!arrayBuffer && !fileUri) {\n throw new Error(\"Either arrayBuffer or fileUri must be provided\");\n }\n\n if (!arrayBuffer) {\n console.log(`fetching fileUri`, fileUri);\n const response = await fetch(fileUri!);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch fileUri: ${response.statusText}`);\n }\n\n arrayBuffer = (await response.arrayBuffer()).slice(0);\n console.log(`fetched fileUri`, arrayBuffer.byteLength, arrayBuffer);\n }\n\n // Create a new copy of the ArrayBuffer to avoid detachment issues\n const bufferCopy = arrayBuffer.slice(0);\n console.log(\n `extractAudioAnalysis skipWavHeader=${skipWavHeader} bitDepth=${bitDepth} len=${bufferCopy.byteLength}`,\n bufferCopy.slice(0, 100),\n );\n\n let actualBitDepth = bitDepth;\n if (!actualBitDepth) {\n console.log(\n `extractAudioAnalysis bitDepth not provided -- getting wav file info`,\n );\n const fileInfo = await getWavFileInfo(bufferCopy);\n actualBitDepth = fileInfo.bitDepth;\n }\n console.log(`extractAudioAnalysis actualBitDepth=${actualBitDepth}`);\n // let copyChannelData: Float32Array;\n // try {\n // const audioContext = new (window.AudioContext ||\n // // @ts-ignore\n // window.webkitAudioContext)();\n // const audioBuffer = await audioContext.decodeAudioData(bufferCopy);\n // const channelData = audioBuffer.getChannelData(0); // Use only the first channel\n // copyChannelData = new Float32Array(channelData); // Create a new Float32Array\n // } catch (error) {\n // console.warn(\"Failed to decode audio data:\", error);\n // // Fall back to creating a new Float32Array from the ArrayBuffer if decoding fails\n // copyChannelData = new Float32Array(arrayBuffer);\n // }\n\n const {\n pcmValues: channelData,\n min,\n max,\n } = convertPCMToFloat32({\n buffer: arrayBuffer,\n bitDepth: actualBitDepth,\n skipWavHeader,\n });\n console.log(\n `extractAudioAnalysis skipWaveHeader=${skipWavHeader} convertPCMToFloat32 length=${channelData.length} range: [ ${min} :: ${max} ]`,\n );\n\n return new Promise((resolve, reject) => {\n const worker = new Worker(\n new URL(featuresExtratorUrl, window.location.href),\n );\n\n worker.onmessage = (event) => {\n resolve(event.data.result);\n };\n\n worker.onerror = (error) => {\n reject(error);\n };\n\n worker.postMessage({\n command: \"process\",\n channelData,\n sampleRate,\n pointsPerSecond,\n algorithm,\n bitDepth,\n fullAudioDurationMs: durationMs,\n numberOfChannels,\n });\n });\n } else {\n if (!fileUri) {\n throw new Error(\"fileUri is required\");\n }\n console.log(`extractAudioAnalysis`, {\n fileUri,\n pointsPerSecond,\n algorithm,\n });\n const res = await ExpoAudioStreamModule.extractAudioAnalysis({\n fileUri,\n pointsPerSecond,\n skipWavHeader,\n algorithm,\n features,\n });\n console.log(`extractAudioAnalysis`, res);\n return res;\n }\n};\n\nexport interface ExtractWaveformProps {\n fileUri: string;\n numberOfSamples: number;\n offset?: number;\n length?: number;\n}\nexport const extractWaveform = async ({\n fileUri,\n numberOfSamples,\n offset = 0,\n length,\n}: ExtractWaveformProps): Promise<unknown> => {\n const res = await ExpoAudioStreamModule.extractAudioAnalysis({\n fileUri,\n numberOfSamples,\n offset,\n length,\n });\n console.log(`extractWaveform`, res);\n return res;\n};\n\nexport {\n AudioRecorderProvider,\n convertPCMToFloat32,\n getWavFileInfo,\n useAudioRecorder,\n useSharedAudioRecorder,\n writeWavHeader as writeWaveHeader,\n};\n\nexport * from \"./ExpoAudioStream.types\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAe;AAEf,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,cAAc,wBAAwB,CAAC;AACvC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AAEvC,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,GACvB,CAAC","sourcesContent":["// src/index.ts\n\nimport { extractAudioAnalysis } from \"./AudioAnalysis/extractAudioAnalysis\";\nimport {\n AudioRecorderProvider,\n useSharedAudioRecorder,\n} from \"./AudioRecorder.provider\";\nimport { useAudioRecorder } from \"./useAudioRecorder\";\n\nexport * from \"./utils/getWavFileInfo\";\nexport * from \"./utils/convertPCMToFloat32\";\nexport * from \"./utils/writeWavHeader\";\n\nexport {\n AudioRecorderProvider,\n extractAudioAnalysis,\n useAudioRecorder,\n useSharedAudioRecorder,\n};\n\nexport type * from \"./AudioAnalysis/AudioAnalysis.types\";\nexport type * from \"./ExpoAudioStream.types\";\n"]}
@@ -0,0 +1,9 @@
1
+ type ConsoleLike = {
2
+ log: (message: string, ...args: unknown[]) => void;
3
+ debug: (message: string, ...args: unknown[]) => void;
4
+ };
5
+ export declare const getLogger: (tag: string) => ConsoleLike;
6
+ export declare const enableAllLoggers: () => void;
7
+ export declare const disableAllLoggers: () => void;
8
+ export {};
9
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAKA,KAAK,WAAW,GAAG;IACjB,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACnD,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACtD,CAAC;AAEF,eAAO,MAAM,SAAS,QAAS,MAAM,KAAG,WAOvC,CAAC;AAEF,eAAO,MAAM,gBAAgB,YAE5B,CAAC;AAEF,eAAO,MAAM,iBAAiB,YAE7B,CAAC"}
@@ -0,0 +1,17 @@
1
+ // packages/expo-audio-stream/src/logger.ts
2
+ import createDebug from "debug";
3
+ import { DEBUG_NAMESPACE } from "./constants";
4
+ export const getLogger = (tag) => {
5
+ const baseLogger = createDebug(`${DEBUG_NAMESPACE}:${tag}`);
6
+ return {
7
+ log: (...args) => baseLogger(...args),
8
+ debug: (...args) => baseLogger(...args),
9
+ };
10
+ };
11
+ export const enableAllLoggers = () => {
12
+ createDebug.enable(`${DEBUG_NAMESPACE}:*`);
13
+ };
14
+ export const disableAllLoggers = () => {
15
+ createDebug.disable();
16
+ };
17
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,OAAO,WAAW,MAAM,OAAO,CAAC;AAEhC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAO9C,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAW,EAAe,EAAE;IACpD,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,eAAe,IAAI,GAAG,EAAE,CAAC,CAAC;IAE5D,OAAO;QACL,GAAG,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE,CAAC,UAAU,CAAC,GAAI,IAAkB,CAAC;QAC/D,KAAK,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE,CAAC,UAAU,CAAC,GAAI,IAAkB,CAAC;KAClE,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,EAAE;IACnC,WAAW,CAAC,MAAM,CAAC,GAAG,eAAe,IAAI,CAAC,CAAC;AAC7C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,EAAE;IACpC,WAAW,CAAC,OAAO,EAAE,CAAC;AACxB,CAAC,CAAC","sourcesContent":["// packages/expo-audio-stream/src/logger.ts\nimport createDebug from \"debug\";\n\nimport { DEBUG_NAMESPACE } from \"./constants\";\n\ntype ConsoleLike = {\n log: (message: string, ...args: unknown[]) => void;\n debug: (message: string, ...args: unknown[]) => void;\n};\n\nexport const getLogger = (tag: string): ConsoleLike => {\n const baseLogger = createDebug(`${DEBUG_NAMESPACE}:${tag}`);\n\n return {\n log: (...args: unknown[]) => baseLogger(...(args as [unknown])),\n debug: (...args: unknown[]) => baseLogger(...(args as [unknown])),\n };\n};\n\nexport const enableAllLoggers = () => {\n createDebug.enable(`${DEBUG_NAMESPACE}:*`);\n};\n\nexport const disableAllLoggers = () => {\n createDebug.disable();\n};\n"]}
@@ -1,5 +1,6 @@
1
- import { AudioAnalysisData, AudioFeaturesOptions, AudioStreamResult, RecordingConfig, StartAudioStreamResult } from "./ExpoAudioStream.types";
2
- import { WavFileInfo } from "./utils";
1
+ import { AudioAnalysisData, AudioFeaturesOptions } from "./AudioAnalysis/AudioAnalysis.types";
2
+ import { AudioRecordingResult, RecordingConfig, StartRecordingResult } from "./ExpoAudioStream.types";
3
+ import { WavFileInfo } from "./utils/getWavFileInfo";
3
4
  export interface ExtractMetadataProps {
4
5
  fileUri?: string;
5
6
  wavMetadata?: WavFileInfo;
@@ -22,8 +23,8 @@ export interface UseAudioRecorderProps {
22
23
  featuresExtratorUrl?: string;
23
24
  }
24
25
  export interface UseAudioRecorderState {
25
- startRecording: (_: RecordingConfig) => Promise<StartAudioStreamResult>;
26
- stopRecording: () => Promise<AudioStreamResult | null>;
26
+ startRecording: (_: RecordingConfig) => Promise<StartRecordingResult>;
27
+ stopRecording: () => Promise<AudioRecordingResult | null>;
27
28
  pauseRecording: () => void;
28
29
  resumeRecording: () => void;
29
30
  isRecording: boolean;
@@ -31,8 +32,6 @@ export interface UseAudioRecorderState {
31
32
  durationMs: number;
32
33
  size: number;
33
34
  analysisData?: AudioAnalysisData;
34
- audioWorkletUrl?: string;
35
- featuresExtratorUrl?: string;
36
35
  }
37
36
  export declare function useAudioRecorder({ debug, audioWorkletUrl, featuresExtratorUrl, }?: UseAudioRecorderProps): UseAudioRecorderState;
38
- //# sourceMappingURL=useAudioRecording.d.ts.map
37
+ //# sourceMappingURL=useAudioRecorder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAudioRecorder.d.ts","sourceRoot":"","sources":["../src/useAudioRecorder.tsx"],"names":[],"mappings":"AAIA,OAAO,EACL,iBAAiB,EAEjB,oBAAoB,EACrB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAGL,oBAAoB,EAEpB,eAAe,EACf,oBAAoB,EACrB,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAKrD,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACtE,aAAa,EAAE,MAAM,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IAC1D,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,iBAAiB,CAAC;CAClC;AAiED,wBAAgB,gBAAgB,CAAC,EAC/B,KAAa,EACb,eAAe,EACf,mBAAmB,GACpB,GAAE,qBAA0B,GAAG,qBAAqB,CA+RpD"}
@@ -1,10 +1,13 @@
1
- // src/useAudioRecording.ts
1
+ // src/useAudioRecorder.ts
2
2
  import { Platform } from "expo-modules-core";
3
3
  import { useCallback, useEffect, useReducer, useRef } from "react";
4
- import { addAudioAnalysisListener, addAudioEventListener } from ".";
5
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);
6
9
  const defaultAnalysis = {
7
- pointsPerSecond: 20,
10
+ pointsPerSecond: 10,
8
11
  bitDepth: 32,
9
12
  numberOfChannels: 1,
10
13
  durationMs: 0,
@@ -16,7 +19,7 @@ const defaultAnalysis = {
16
19
  max: Number.NEGATIVE_INFINITY,
17
20
  },
18
21
  };
19
- function recorderReducer(state, action) {
22
+ function audioRecorderReducer(state, action) {
20
23
  switch (action.type) {
21
24
  case "START":
22
25
  return {
@@ -48,9 +51,8 @@ function recorderReducer(state, action) {
48
51
  return state;
49
52
  }
50
53
  }
51
- const TAG = "[ useAudioRecorder ] ";
52
54
  export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtratorUrl, } = {}) {
53
- const [state, dispatch] = useReducer(recorderReducer, {
55
+ const [state, dispatch] = useReducer(audioRecorderReducer, {
54
56
  isRecording: false,
55
57
  isPaused: false,
56
58
  durationMs: 0,
@@ -64,20 +66,10 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
64
66
  ? ExpoAudioStreamModule({ audioWorkletUrl, featuresExtratorUrl })
65
67
  : ExpoAudioStreamModule;
66
68
  const onAudioStreamRef = useRef(null);
67
- const logDebug = useCallback((message, data) => {
68
- if (debug) {
69
- if (data) {
70
- console.log(`${TAG} ${message}`, data);
71
- }
72
- else {
73
- console.log(`${TAG} ${message}`);
74
- }
75
- }
76
- }, [debug]);
77
- const handleAudioAnalysis = useCallback(async (analysis, visualizationDuration) => {
69
+ const handleAudioAnalysis = useCallback(async ({ analysis, visualizationDuration }) => {
78
70
  const savedAnalysisData = analysisRef.current || { ...defaultAnalysis };
79
71
  const maxDuration = visualizationDuration;
80
- logDebug(`[handleAudioAnalysis] Received audio analysis: maxDuration=${maxDuration} analysis.dataPoints=${analysis.dataPoints.length} analysisData.dataPoints=${savedAnalysisData.dataPoints.length}`, analysis);
72
+ logger.debug(`[handleAudioAnalysis] Received audio analysis: maxDuration=${maxDuration} analysis.dataPoints=${analysis.dataPoints.length} analysisData.dataPoints=${savedAnalysisData.dataPoints.length}`, analysis);
81
73
  // Combine data points
82
74
  const combinedDataPoints = [
83
75
  ...savedAnalysisData.dataPoints,
@@ -86,7 +78,7 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
86
78
  // Calculate the new duration
87
79
  const pointsPerSecond = analysis.pointsPerSecond || savedAnalysisData.pointsPerSecond;
88
80
  const maxDataPoints = (pointsPerSecond * visualizationDuration) / 1000;
89
- logDebug(`[handleAudioAnalysis] Combined data points before trimming: pointsPerSecond=${pointsPerSecond} visualizationDuration=${visualizationDuration} combinedDataPointsLength=${combinedDataPoints.length} vs maxDataPoints=${maxDataPoints}`);
81
+ logger.debug(`[handleAudioAnalysis] Combined data points before trimming: pointsPerSecond=${pointsPerSecond} visualizationDuration=${visualizationDuration} combinedDataPointsLength=${combinedDataPoints.length} vs maxDataPoints=${maxDataPoints}`);
90
82
  // Trim data points to keep within the maximum number of data points
91
83
  if (combinedDataPoints.length > maxDataPoints) {
92
84
  combinedDataPoints.splice(0, combinedDataPoints.length - maxDataPoints);
@@ -103,16 +95,16 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
103
95
  min: newMin,
104
96
  max: newMax,
105
97
  };
106
- logDebug(`[handleAudioAnalysis] Updated analysis data: durationMs=${savedAnalysisData.durationMs}`, savedAnalysisData);
98
+ logger.debug(`[handleAudioAnalysis] Updated analysis data: durationMs=${savedAnalysisData.durationMs}`, savedAnalysisData);
107
99
  // Update the ref
108
100
  analysisRef.current = savedAnalysisData;
109
101
  // Dispatch the updated analysis data to state to trigger re-render
110
102
  // need to use spread operator otherwise it doesnt trigger update.
111
103
  dispatch({ type: "UPDATE_ANALYSIS", payload: { ...savedAnalysisData } });
112
- }, [logDebug]);
104
+ }, [dispatch]);
113
105
  const handleAudioEvent = useCallback(async (eventData) => {
114
106
  const { fileUri, deltaSize, totalSize, lastEmittedSize, position, streamUuid, encoded, mimeType, buffer, } = eventData;
115
- logDebug(`[handleAudioEvent] Received audio event:`, {
107
+ logger.debug(`[handleAudioEvent] Received audio event:`, {
116
108
  fileUri,
117
109
  deltaSize,
118
110
  totalSize,
@@ -144,28 +136,30 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
144
136
  }
145
137
  else if (buffer) {
146
138
  // Coming from web
147
- onAudioStreamRef.current?.({
139
+ const webEvent = {
148
140
  data: buffer,
149
141
  position,
150
142
  fileUri,
151
143
  eventDataSize: deltaSize,
152
144
  totalSize,
153
- });
145
+ };
146
+ onAudioStreamRef.current?.(webEvent);
147
+ logger.debug(`[handleAudioEvent] Audio data sent to onAudioStream`, webEvent);
154
148
  }
155
149
  }
156
150
  catch (error) {
157
151
  console.error(`${TAG} Error processing audio event:`, error);
158
152
  }
159
- }, [logDebug]);
153
+ }, []);
160
154
  const checkStatus = useCallback(async () => {
161
155
  try {
162
156
  if (!state.isRecording) {
163
- logDebug(`${TAG} Not recording, exiting status check.`);
157
+ logger.debug(`Not recording, exiting status check.`);
164
158
  return;
165
159
  }
166
160
  const status = ExpoAudioStream.status();
167
161
  if (debug) {
168
- logDebug(`${TAG} Status:`, status);
162
+ logger.debug(`Status:`, status);
169
163
  }
170
164
  dispatch({
171
165
  type: "UPDATE_STATUS",
@@ -175,36 +169,13 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
175
169
  catch (error) {
176
170
  console.error(`${TAG} Error getting status:`, error);
177
171
  }
178
- }, [state.isRecording, logDebug]);
179
- useEffect(() => {
180
- let interval;
181
- if (state.isRecording) {
182
- interval = setInterval(checkStatus, 1000);
183
- }
184
- return () => {
185
- if (interval) {
186
- clearInterval(interval);
187
- }
188
- };
189
- }, [checkStatus, state.isRecording]);
190
- useEffect(() => {
191
- logDebug(`Registering audio event listener`);
192
- const subscribeAudio = addAudioEventListener(handleAudioEvent);
193
- logDebug(`Subscribed to audio event listener and analysis listener`, {
194
- subscribeAudio,
195
- });
196
- return () => {
197
- logDebug(`Removing audio event listener`);
198
- subscribeAudio.remove();
199
- };
200
- }, [handleAudioEvent, handleAudioAnalysis, logDebug]);
172
+ }, [state.isRecording]);
201
173
  const startRecording = useCallback(async (recordingOptions) => {
202
- if (debug) {
203
- logDebug(`start recoding`, recordingOptions);
204
- }
174
+ logger.debug(`start recoding`, recordingOptions);
205
175
  analysisRef.current = { ...defaultAnalysis }; // Reset analysis data
206
176
  const { onAudioStream, ...options } = recordingOptions;
207
- const { maxRecentDataDuration = 10000, enableProcessing } = options;
177
+ const { enableProcessing } = options;
178
+ const maxRecentDataDuration = 10000; // TODO compute maxRecentDataDuration based on screen dimensions
208
179
  if (typeof onAudioStream === "function") {
209
180
  onAudioStreamRef.current = onAudioStream;
210
181
  }
@@ -215,10 +186,13 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
215
186
  const startResult = await ExpoAudioStream.startRecording(options);
216
187
  dispatch({ type: "START" });
217
188
  if (enableProcessing) {
218
- logDebug(`Enabling audio analysis listener`);
189
+ logger.debug(`Enabling audio analysis listener`);
219
190
  const listener = addAudioAnalysisListener(async (analysisData) => {
220
191
  try {
221
- await handleAudioAnalysis(analysisData, maxRecentDataDuration);
192
+ await handleAudioAnalysis({
193
+ analysis: analysisData,
194
+ visualizationDuration: maxRecentDataDuration,
195
+ });
222
196
  }
223
197
  catch (error) {
224
198
  console.warn(`${TAG} Error processing audio analysis:`, error);
@@ -227,31 +201,61 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
227
201
  analysisListenerRef.current = listener;
228
202
  }
229
203
  return startResult;
230
- }, [logDebug]);
204
+ }, [handleAudioAnalysis, dispatch]);
231
205
  const stopRecording = useCallback(async () => {
232
- logDebug(`${TAG} stoping recording`);
206
+ logger.debug(`stoping recording`);
233
207
  if (analysisListenerRef.current) {
234
208
  analysisListenerRef.current.remove();
235
209
  analysisListenerRef.current = null;
236
210
  }
237
211
  const stopResult = await ExpoAudioStream.stopRecording();
238
212
  onAudioStreamRef.current = null;
239
- logDebug(`${TAG} recording stopped`, stopResult);
213
+ logger.debug(`recording stopped`, stopResult);
240
214
  dispatch({ type: "STOP" });
241
215
  return stopResult;
242
- }, [logDebug]);
216
+ }, [dispatch]);
243
217
  const pauseRecording = useCallback(async () => {
244
- logDebug(`${TAG} pause recording`);
218
+ logger.debug(`pause recording`);
245
219
  const pauseResult = await ExpoAudioStream.pauseRecording();
246
220
  dispatch({ type: "PAUSE" });
247
221
  return pauseResult;
248
- }, [logDebug]);
222
+ }, [dispatch]);
249
223
  const resumeRecording = useCallback(async () => {
250
- logDebug(`${TAG} resume recording`);
224
+ logger.debug(`resume recording`);
251
225
  const resumeResult = await ExpoAudioStream.resumeRecording();
252
226
  dispatch({ type: "RESUME" });
253
227
  return resumeResult;
254
- }, [logDebug]);
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]);
255
259
  return {
256
260
  startRecording,
257
261
  stopRecording,
@@ -264,4 +268,4 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
264
268
  analysisData: state.analysisData,
265
269
  };
266
270
  }
267
- //# sourceMappingURL=useAudioRecording.js.map
271
+ //# sourceMappingURL=useAudioRecorder.js.map
@@ -0,0 +1 @@
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"]}
@@ -0,0 +1,11 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,41 @@
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
@@ -0,0 +1 @@
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"]}
@@ -0,0 +1,5 @@
1
+ import { BitDepth, EncodingType } from "../ExpoAudioStream.types";
2
+ export declare const encodingToBitDepth: ({ encoding, }: {
3
+ encoding: EncodingType;
4
+ }) => BitDepth;
5
+ //# sourceMappingURL=encodingToBitDepth.d.ts.map
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,13 @@
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
@@ -0,0 +1 @@
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"]}
@@ -0,0 +1,25 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,89 @@
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