@siteed/expo-audio-stream 1.0.3 → 1.0.5

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 (114) hide show
  1. package/.size-limit.json +4 -4
  2. package/README.md +18 -176
  3. package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +1 -0
  4. package/app.plugin.js +1 -1
  5. package/build/AudioAnalysis/AudioAnalysis.types.d.ts +3 -5
  6. package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -1
  7. package/build/AudioAnalysis/AudioAnalysis.types.js.map +1 -1
  8. package/build/AudioAnalysis/extractAudioAnalysis.d.ts +19 -3
  9. package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -1
  10. package/build/AudioAnalysis/extractAudioAnalysis.js +14 -27
  11. package/build/AudioAnalysis/extractAudioAnalysis.js.map +1 -1
  12. package/build/AudioAnalysis/extractWaveform.d.ts.map +1 -1
  13. package/build/AudioAnalysis/extractWaveform.js +3 -3
  14. package/build/AudioAnalysis/extractWaveform.js.map +1 -1
  15. package/build/AudioRecorder.provider.d.ts +6 -6
  16. package/build/AudioRecorder.provider.d.ts.map +1 -1
  17. package/build/AudioRecorder.provider.js +9 -9
  18. package/build/AudioRecorder.provider.js.map +1 -1
  19. package/build/ExpoAudioStream.native.d.ts.map +1 -1
  20. package/build/ExpoAudioStream.native.js +2 -2
  21. package/build/ExpoAudioStream.native.js.map +1 -1
  22. package/build/ExpoAudioStream.types.d.ts +20 -18
  23. package/build/ExpoAudioStream.types.d.ts.map +1 -1
  24. package/build/ExpoAudioStream.types.js.map +1 -1
  25. package/build/ExpoAudioStream.web.d.ts +8 -8
  26. package/build/ExpoAudioStream.web.d.ts.map +1 -1
  27. package/build/ExpoAudioStream.web.js +40 -22
  28. package/build/ExpoAudioStream.web.js.map +1 -1
  29. package/build/ExpoAudioStreamModule.d.ts.map +1 -1
  30. package/build/ExpoAudioStreamModule.js +8 -7
  31. package/build/ExpoAudioStreamModule.js.map +1 -1
  32. package/build/WebRecorder.web.d.ts +8 -8
  33. package/build/WebRecorder.web.d.ts.map +1 -1
  34. package/build/WebRecorder.web.js +60 -50
  35. package/build/WebRecorder.web.js.map +1 -1
  36. package/build/constants.d.ts +1 -1
  37. package/build/constants.d.ts.map +1 -1
  38. package/build/constants.js +3 -3
  39. package/build/constants.js.map +1 -1
  40. package/build/events.d.ts +16 -4
  41. package/build/events.d.ts.map +1 -1
  42. package/build/events.js +8 -8
  43. package/build/events.js.map +1 -1
  44. package/build/index.d.ts +8 -8
  45. package/build/index.d.ts.map +1 -1
  46. package/build/index.js +6 -6
  47. package/build/index.js.map +1 -1
  48. package/build/logger.d.ts +2 -2
  49. package/build/logger.d.ts.map +1 -1
  50. package/build/logger.js +7 -11
  51. package/build/logger.js.map +1 -1
  52. package/build/useAudioRecorder.d.ts +4 -21
  53. package/build/useAudioRecorder.d.ts.map +1 -1
  54. package/build/useAudioRecorder.js +33 -33
  55. package/build/useAudioRecorder.js.map +1 -1
  56. package/build/utils/BlobFix.d.ts +9 -0
  57. package/build/utils/BlobFix.d.ts.map +1 -0
  58. package/build/utils/BlobFix.js +494 -0
  59. package/build/utils/BlobFix.js.map +1 -0
  60. package/build/utils/concatenateBuffers.d.ts +8 -0
  61. package/build/utils/concatenateBuffers.d.ts.map +1 -0
  62. package/build/utils/concatenateBuffers.js +21 -0
  63. package/build/utils/concatenateBuffers.js.map +1 -0
  64. package/build/utils/convertPCMToFloat32.d.ts +2 -2
  65. package/build/utils/convertPCMToFloat32.d.ts.map +1 -1
  66. package/build/utils/convertPCMToFloat32.js +49 -36
  67. package/build/utils/convertPCMToFloat32.js.map +1 -1
  68. package/build/utils/encodingToBitDepth.d.ts +1 -1
  69. package/build/utils/encodingToBitDepth.d.ts.map +1 -1
  70. package/build/utils/encodingToBitDepth.js +3 -3
  71. package/build/utils/encodingToBitDepth.js.map +1 -1
  72. package/build/utils/getWavFileInfo.d.ts +4 -3
  73. package/build/utils/getWavFileInfo.d.ts.map +1 -1
  74. package/build/utils/getWavFileInfo.js +18 -15
  75. package/build/utils/getWavFileInfo.js.map +1 -1
  76. package/build/utils/writeWavHeader.d.ts.map +1 -1
  77. package/build/utils/writeWavHeader.js +4 -4
  78. package/build/utils/writeWavHeader.js.map +1 -1
  79. package/build/workers/InlineFeaturesExtractor.web.d.ts.map +1 -1
  80. package/build/workers/InlineFeaturesExtractor.web.js.map +1 -1
  81. package/build/workers/inlineAudioWebWorker.web.d.ts.map +1 -1
  82. package/build/workers/inlineAudioWebWorker.web.js.map +1 -1
  83. package/expo-module.config.json +8 -17
  84. package/ios/AudioStreamManager.swift +1 -0
  85. package/ios/ExpoAudioStreamModule.swift +1 -0
  86. package/ios/RecordingResult.swift +1 -0
  87. package/package.json +72 -65
  88. package/plugin/build/index.d.ts +1 -1
  89. package/plugin/build/index.js +7 -7
  90. package/plugin/src/index.ts +47 -47
  91. package/plugin/tsconfig.json +8 -13
  92. package/src/AudioAnalysis/AudioAnalysis.types.ts +59 -60
  93. package/src/AudioAnalysis/extractAudioAnalysis.ts +132 -121
  94. package/src/AudioAnalysis/extractWaveform.ts +18 -18
  95. package/src/AudioRecorder.provider.tsx +53 -53
  96. package/src/ExpoAudioStream.native.ts +2 -2
  97. package/src/ExpoAudioStream.types.ts +56 -53
  98. package/src/ExpoAudioStream.web.ts +232 -205
  99. package/src/ExpoAudioStreamModule.ts +17 -16
  100. package/src/WebRecorder.web.ts +407 -390
  101. package/src/constants.ts +11 -11
  102. package/src/events.ts +27 -13
  103. package/src/index.ts +15 -15
  104. package/src/logger.ts +15 -18
  105. package/src/useAudioRecorder.tsx +394 -389
  106. package/src/utils/BlobFix.ts +550 -0
  107. package/src/utils/concatenateBuffers.ts +24 -0
  108. package/src/utils/convertPCMToFloat32.ts +72 -45
  109. package/src/utils/encodingToBitDepth.ts +14 -14
  110. package/src/utils/getWavFileInfo.ts +106 -99
  111. package/src/utils/writeWavHeader.ts +45 -45
  112. package/src/workers/InlineFeaturesExtractor.web.tsx +1 -1
  113. package/src/workers/inlineAudioWebWorker.web.tsx +1 -1
  114. package/tsconfig.json +12 -7
@@ -1 +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
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,OAAO,EAAE,SAAS,IAAI,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAE1E,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAS7C,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAW,EAAe,EAAE;IAClD,MAAM,UAAU,GAAG,eAAe,CAAC,GAAG,eAAe,IAAI,GAAG,EAAE,CAAC,CAAA;IAE/D,OAAO;QACH,GAAG,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,GAAI,IAAkB,CAAC;QACnE,KAAK,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,GAAI,IAAkB,CAAC;QACvE,KAAK,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,GAAI,IAAkB,CAAC;QACvE,IAAI,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,GAAI,IAAkB,CAAC;KACxE,CAAA;AACL,CAAC,CAAA","sourcesContent":["// packages/expo-audio-stream/src/logger.ts\nimport { getLogger as siteedGetLogger } from '@siteed/react-native-logger'\n\nimport { DEBUG_NAMESPACE } from './constants'\n\ntype ConsoleLike = {\n log: (message: string, ...args: unknown[]) => void\n debug: (message: string, ...args: unknown[]) => void\n warn: (message: string, ...args: unknown[]) => void\n error: (message: string, ...args: unknown[]) => void\n}\n\nexport const getLogger = (tag: string): ConsoleLike => {\n const baseLogger = siteedGetLogger(`${DEBUG_NAMESPACE}:${tag}`)\n\n return {\n log: (...args: unknown[]) => baseLogger.log(...(args as [unknown])),\n debug: (...args: unknown[]) => baseLogger.debug(...(args as [unknown])),\n error: (...args: unknown[]) => baseLogger.error(...(args as [unknown])),\n warn: (...args: unknown[]) => baseLogger.warn(...(args as [unknown])),\n }\n}\n\n"]}
@@ -1,22 +1,5 @@
1
- import { AudioAnalysisData, AudioFeaturesOptions } from "./AudioAnalysis/AudioAnalysis.types";
2
- import { AudioRecordingResult, RecordingConfig, StartRecordingResult } from "./ExpoAudioStream.types";
3
- import { WavFileInfo } from "./utils/getWavFileInfo";
4
- export interface ExtractMetadataProps {
5
- fileUri?: string;
6
- wavMetadata?: WavFileInfo;
7
- arrayBuffer?: ArrayBuffer;
8
- bitDepth?: number;
9
- skipWavHeader?: boolean;
10
- durationMs?: number;
11
- sampleRate?: number;
12
- numberOfChannels?: number;
13
- algorithm?: "peak" | "rms";
14
- position?: number;
15
- length?: number;
16
- pointsPerSecond?: number;
17
- features?: AudioFeaturesOptions;
18
- featuresExtratorUrl?: string;
19
- }
1
+ import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types';
2
+ import { AudioRecording, RecordingConfig, StartRecordingResult } from './ExpoAudioStream.types';
20
3
  export interface UseAudioRecorderProps {
21
4
  debug?: boolean;
22
5
  audioWorkletUrl?: string;
@@ -24,14 +7,14 @@ export interface UseAudioRecorderProps {
24
7
  }
25
8
  export interface UseAudioRecorderState {
26
9
  startRecording: (_: RecordingConfig) => Promise<StartRecordingResult>;
27
- stopRecording: () => Promise<AudioRecordingResult | null>;
10
+ stopRecording: () => Promise<AudioRecording>;
28
11
  pauseRecording: () => void;
29
12
  resumeRecording: () => void;
30
13
  isRecording: boolean;
31
14
  isPaused: boolean;
32
15
  durationMs: number;
33
16
  size: number;
34
- analysisData?: AudioAnalysisData;
17
+ analysisData?: AudioAnalysis;
35
18
  }
36
19
  export declare function useAudioRecorder({ debug, audioWorkletUrl, featuresExtratorUrl, }?: UseAudioRecorderProps): UseAudioRecorderState;
37
20
  //# sourceMappingURL=useAudioRecorder.d.ts.map
@@ -1 +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
+ {"version":3,"file":"useAudioRecorder.d.ts","sourceRoot":"","sources":["../src/useAudioRecorder.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AACnE,OAAO,EAEH,cAAc,EAEd,eAAe,EACf,oBAAoB,EACvB,MAAM,yBAAyB,CAAA;AAWhC,MAAM,WAAW,qBAAqB;IAClC,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC/B;AAED,MAAM,WAAW,qBAAqB;IAClC,cAAc,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAA;IACrE,aAAa,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAA;IAC5C,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,WAAW,EAAE,OAAO,CAAA;IACpB,QAAQ,EAAE,OAAO,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,CAAC,EAAE,aAAa,CAAA;CAC/B;AAwED,wBAAgB,gBAAgB,CAAC,EAC7B,KAAa,EACb,eAAe,EACf,mBAAmB,GACtB,GAAE,qBAA0B,GAAG,qBAAqB,CAiTpD"}
@@ -1,10 +1,10 @@
1
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";
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 { getLogger } from './logger';
7
+ const TAG = 'useAudioRecorder';
8
8
  const logger = getLogger(TAG);
9
9
  const defaultAnalysis = {
10
10
  pointsPerSecond: 10,
@@ -14,6 +14,8 @@ const defaultAnalysis = {
14
14
  sampleRate: 44100,
15
15
  samples: 0,
16
16
  dataPoints: [],
17
+ amplitudeAlgorithm: 'rms',
18
+ speakerChanges: [],
17
19
  amplitudeRange: {
18
20
  min: Number.POSITIVE_INFINITY,
19
21
  max: Number.NEGATIVE_INFINITY,
@@ -21,7 +23,7 @@ const defaultAnalysis = {
21
23
  };
22
24
  function audioRecorderReducer(state, action) {
23
25
  switch (action.type) {
24
- case "START":
26
+ case 'START':
25
27
  return {
26
28
  ...state,
27
29
  isRecording: true,
@@ -30,19 +32,19 @@ function audioRecorderReducer(state, action) {
30
32
  size: 0,
31
33
  analysisData: defaultAnalysis, // Reset analysis data
32
34
  };
33
- case "STOP":
35
+ case 'STOP':
34
36
  return { ...state, isRecording: false, isPaused: false };
35
- case "PAUSE":
37
+ case 'PAUSE':
36
38
  return { ...state, isPaused: true, isRecording: false };
37
- case "RESUME":
39
+ case 'RESUME':
38
40
  return { ...state, isPaused: false, isRecording: true };
39
- case "UPDATE_STATUS":
41
+ case 'UPDATE_STATUS':
40
42
  return {
41
43
  ...state,
42
44
  durationMs: action.payload.durationMs,
43
45
  size: action.payload.size,
44
46
  };
45
- case "UPDATE_ANALYSIS":
47
+ case 'UPDATE_ANALYSIS':
46
48
  return {
47
49
  ...state,
48
50
  analysisData: action.payload,
@@ -62,12 +64,14 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
62
64
  const analysisListenerRef = useRef(null);
63
65
  const analysisRef = useRef({ ...defaultAnalysis });
64
66
  // Instantiate the module for web with URLs
65
- const ExpoAudioStream = Platform.OS === "web"
67
+ const ExpoAudioStream = Platform.OS === 'web'
66
68
  ? ExpoAudioStreamModule({ audioWorkletUrl, featuresExtratorUrl })
67
69
  : ExpoAudioStreamModule;
68
70
  const onAudioStreamRef = useRef(null);
69
- const handleAudioAnalysis = useCallback(async ({ analysis, visualizationDuration }) => {
70
- const savedAnalysisData = analysisRef.current || { ...defaultAnalysis };
71
+ const handleAudioAnalysis = useCallback(async ({ analysis, visualizationDuration, }) => {
72
+ const savedAnalysisData = analysisRef.current || {
73
+ ...defaultAnalysis,
74
+ };
71
75
  const maxDuration = visualizationDuration;
72
76
  logger.debug(`[handleAudioAnalysis] Received audio analysis: maxDuration=${maxDuration} analysis.dataPoints=${analysis.dataPoints.length} analysisData.dataPoints=${savedAnalysisData.dataPoints.length}`, analysis);
73
77
  // Combine data points
@@ -100,7 +104,10 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
100
104
  analysisRef.current = savedAnalysisData;
101
105
  // Dispatch the updated analysis data to state to trigger re-render
102
106
  // need to use spread operator otherwise it doesnt trigger update.
103
- dispatch({ type: "UPDATE_ANALYSIS", payload: { ...savedAnalysisData } });
107
+ dispatch({
108
+ type: 'UPDATE_ANALYSIS',
109
+ payload: { ...savedAnalysisData },
110
+ });
104
111
  }, [dispatch]);
105
112
  const handleAudioEvent = useCallback(async (eventData) => {
106
113
  const { fileUri, deltaSize, totalSize, lastEmittedSize, position, streamUuid, encoded, mimeType, buffer, } = eventData;
@@ -120,11 +127,11 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
120
127
  }
121
128
  try {
122
129
  // Coming from native ( ios / android ) otherwise buffer is set
123
- if (Platform.OS !== "web") {
130
+ if (Platform.OS !== 'web') {
124
131
  // Read the audio file as a base64 string for comparison
125
132
  if (!encoded) {
126
133
  console.error(`${TAG} Encoded audio data is missing`);
127
- throw new Error("Encoded audio data is missing");
134
+ throw new Error('Encoded audio data is missing');
128
135
  }
129
136
  onAudioStreamRef.current?.({
130
137
  data: encoded,
@@ -162,7 +169,7 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
162
169
  logger.debug(`Status:`, status);
163
170
  }
164
171
  dispatch({
165
- type: "UPDATE_STATUS",
172
+ type: 'UPDATE_STATUS',
166
173
  payload: { durationMs: status.durationMs, size: status.size },
167
174
  });
168
175
  }
@@ -176,7 +183,7 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
176
183
  const { onAudioStream, ...options } = recordingOptions;
177
184
  const { enableProcessing } = options;
178
185
  const maxRecentDataDuration = 10000; // TODO compute maxRecentDataDuration based on screen dimensions
179
- if (typeof onAudioStream === "function") {
186
+ if (typeof onAudioStream === 'function') {
180
187
  onAudioStreamRef.current = onAudioStream;
181
188
  }
182
189
  else {
@@ -184,7 +191,7 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
184
191
  onAudioStreamRef.current = null;
185
192
  }
186
193
  const startResult = await ExpoAudioStream.startRecording(options);
187
- dispatch({ type: "START" });
194
+ dispatch({ type: 'START' });
188
195
  if (enableProcessing) {
189
196
  logger.debug(`Enabling audio analysis listener`);
190
197
  const listener = addAudioAnalysisListener(async (analysisData) => {
@@ -204,26 +211,27 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
204
211
  }, [handleAudioAnalysis, dispatch]);
205
212
  const stopRecording = useCallback(async () => {
206
213
  logger.debug(`stoping recording`);
214
+ const stopResult = await ExpoAudioStream.stopRecording();
215
+ stopResult.analysisData = analysisRef.current;
207
216
  if (analysisListenerRef.current) {
208
217
  analysisListenerRef.current.remove();
209
218
  analysisListenerRef.current = null;
210
219
  }
211
- const stopResult = await ExpoAudioStream.stopRecording();
212
220
  onAudioStreamRef.current = null;
213
221
  logger.debug(`recording stopped`, stopResult);
214
- dispatch({ type: "STOP" });
222
+ dispatch({ type: 'STOP' });
215
223
  return stopResult;
216
224
  }, [dispatch]);
217
225
  const pauseRecording = useCallback(async () => {
218
226
  logger.debug(`pause recording`);
219
227
  const pauseResult = await ExpoAudioStream.pauseRecording();
220
- dispatch({ type: "PAUSE" });
228
+ dispatch({ type: 'PAUSE' });
221
229
  return pauseResult;
222
230
  }, [dispatch]);
223
231
  const resumeRecording = useCallback(async () => {
224
232
  logger.debug(`resume recording`);
225
233
  const resumeResult = await ExpoAudioStream.resumeRecording();
226
- dispatch({ type: "RESUME" });
234
+ dispatch({ type: 'RESUME' });
227
235
  return resumeResult;
228
236
  }, [dispatch]);
229
237
  useEffect(() => {
@@ -248,14 +256,6 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
248
256
  subscribeAudio.remove();
249
257
  };
250
258
  }, [handleAudioEvent, handleAudioAnalysis]);
251
- useEffect(() => {
252
- if (debug) {
253
- enableAllLoggers();
254
- }
255
- else {
256
- disableAllLoggers();
257
- }
258
- }, [debug]);
259
259
  return {
260
260
  startRecording,
261
261
  stopRecording,
@@ -1 +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"]}
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 { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types'\nimport {\n AudioDataEvent,\n AudioRecording,\n AudioStreamStatus,\n RecordingConfig,\n StartRecordingResult,\n} from './ExpoAudioStream.types'\nimport ExpoAudioStreamModule from './ExpoAudioStreamModule'\nimport {\n addAudioAnalysisListener,\n addAudioEventListener,\n AudioEventPayload,\n} from './events'\nimport { getLogger } from './logger'\n\nconst TAG = 'useAudioRecorder'\nconst logger = getLogger(TAG)\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<AudioRecording>\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?: AudioAnalysis\n}\n\ninterface RecorderReducerState {\n isRecording: boolean\n isPaused: boolean\n durationMs: number\n size: number\n analysisData?: AudioAnalysis\n}\n\ntype RecorderAction =\n | { type: 'START' | 'STOP' | 'PAUSE' | 'RESUME' }\n | { type: 'UPDATE_STATUS'; payload: { durationMs: number; size: number } }\n | { type: 'UPDATE_ANALYSIS'; payload: AudioAnalysis }\n\nconst defaultAnalysis: AudioAnalysis = {\n pointsPerSecond: 10,\n bitDepth: 32,\n numberOfChannels: 1,\n durationMs: 0,\n sampleRate: 44100,\n samples: 0,\n dataPoints: [],\n amplitudeAlgorithm: 'rms',\n speakerChanges: [],\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\ninterface HandleAudioAnalysisProps {\n analysis: AudioAnalysis\n visualizationDuration: number\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<AudioAnalysis>({ ...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 ({\n analysis,\n visualizationDuration,\n }: HandleAudioAnalysisProps) => {\n const savedAnalysisData = analysisRef.current || {\n ...defaultAnalysis,\n }\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 =\n (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(\n 0,\n combinedDataPoints.length - maxDataPoints\n )\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({\n type: 'UPDATE_ANALYSIS',\n payload: { ...savedAnalysisData },\n })\n },\n [dispatch]\n )\n\n const handleAudioEvent = useCallback(\n 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 )\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(\n `${TAG} onAudioStream is not a function`,\n onAudioStream\n )\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(\n async (analysisData) => {\n try {\n await handleAudioAnalysis({\n analysis: analysisData,\n visualizationDuration: maxRecentDataDuration,\n })\n } catch (error) {\n console.warn(\n `${TAG} Error processing audio analysis:`,\n error\n )\n }\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 const stopResult: AudioRecording = await ExpoAudioStream.stopRecording()\n stopResult.analysisData = analysisRef.current\n\n if (analysisListenerRef.current) {\n analysisListenerRef.current.remove()\n analysisListenerRef.current = null\n }\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(\n `Subscribed to audio event listener and analysis listener`,\n {\n subscribeAudio,\n }\n )\n\n return () => {\n logger.debug(`Removing audio event listener`)\n subscribeAudio.remove()\n }\n }, [handleAudioEvent, handleAudioAnalysis])\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,9 @@
1
+ /**
2
+ * Fixes duration on MediaRecorder output.
3
+ * @param blob Input Blob with incorrect duration.
4
+ * @param duration Correct duration (in milliseconds).
5
+ * @param type Output blob mimetype (default: video/webm).
6
+ * @returns
7
+ */
8
+ export declare const webmFixDuration: (blob: Blob, duration: number, type?: string) => Promise<Blob>;
9
+ //# sourceMappingURL=BlobFix.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlobFix.d.ts","sourceRoot":"","sources":["../../src/utils/BlobFix.ts"],"names":[],"mappings":"AAggBA;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,SAClB,IAAI,YACA,MAAM,oBAEjB,OAAO,CAAC,IAAI,CA0Bd,CAAC"}