@siteed/expo-audio-stream 1.0.2 → 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.
- package/.size-limit.json +6 -0
- package/README.md +18 -176
- package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +1 -0
- package/app.plugin.js +1 -1
- package/build/AudioAnalysis/AudioAnalysis.types.d.ts +74 -0
- package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -0
- package/build/AudioAnalysis/AudioAnalysis.types.js +3 -0
- package/build/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
- package/build/AudioAnalysis/extractAudioAnalysis.d.ts +20 -0
- package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -0
- package/build/AudioAnalysis/extractAudioAnalysis.js +88 -0
- package/build/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
- package/build/AudioAnalysis/extractWaveform.d.ts +8 -0
- package/build/AudioAnalysis/extractWaveform.d.ts.map +1 -0
- package/build/AudioAnalysis/extractWaveform.js +14 -0
- package/build/AudioAnalysis/extractWaveform.js.map +1 -0
- package/build/AudioRecorder.provider.d.ts +15 -2
- package/build/AudioRecorder.provider.d.ts.map +1 -1
- package/build/AudioRecorder.provider.js +21 -8
- package/build/AudioRecorder.provider.js.map +1 -1
- package/build/ExpoAudioStream.native.d.ts.map +1 -1
- package/build/ExpoAudioStream.native.js +2 -2
- package/build/ExpoAudioStream.native.js.map +1 -1
- package/build/ExpoAudioStream.types.d.ts +33 -89
- package/build/ExpoAudioStream.types.d.ts.map +1 -1
- package/build/ExpoAudioStream.types.js.map +1 -1
- package/build/ExpoAudioStream.web.d.ts +10 -9
- package/build/ExpoAudioStream.web.d.ts.map +1 -1
- package/build/ExpoAudioStream.web.js +44 -25
- package/build/ExpoAudioStream.web.js.map +1 -1
- package/build/ExpoAudioStreamModule.d.ts.map +1 -1
- package/build/ExpoAudioStreamModule.js +13 -8
- package/build/ExpoAudioStreamModule.js.map +1 -1
- package/build/{WebRecorder.d.ts → WebRecorder.web.d.ts} +13 -9
- package/build/WebRecorder.web.d.ts.map +1 -0
- package/build/{WebRecorder.js → WebRecorder.web.js} +118 -63
- package/build/WebRecorder.web.js.map +1 -0
- package/build/constants.d.ts +11 -0
- package/build/constants.d.ts.map +1 -0
- package/build/constants.js +14 -0
- package/build/constants.js.map +1 -0
- package/build/events.d.ts +18 -0
- package/build/events.d.ts.map +1 -0
- package/build/events.js +15 -0
- package/build/events.js.map +1 -0
- package/build/index.d.ts +9 -17
- package/build/index.d.ts.map +1 -1
- package/build/index.js +7 -113
- package/build/index.js.map +1 -1
- package/build/logger.d.ts +9 -0
- package/build/logger.d.ts.map +1 -0
- package/build/logger.js +13 -0
- package/build/logger.js.map +1 -0
- package/build/useAudioRecorder.d.ts +20 -0
- package/build/useAudioRecorder.d.ts.map +1 -0
- package/build/{useAudioRecording.js → useAudioRecorder.js} +90 -86
- package/build/useAudioRecorder.js.map +1 -0
- package/build/utils/BlobFix.d.ts +9 -0
- package/build/utils/BlobFix.d.ts.map +1 -0
- package/build/utils/BlobFix.js +494 -0
- package/build/utils/BlobFix.js.map +1 -0
- package/build/utils/concatenateBuffers.d.ts +8 -0
- package/build/utils/concatenateBuffers.d.ts.map +1 -0
- package/build/utils/concatenateBuffers.js +21 -0
- package/build/utils/concatenateBuffers.js.map +1 -0
- package/build/utils/convertPCMToFloat32.d.ts +11 -0
- package/build/utils/convertPCMToFloat32.d.ts.map +1 -0
- package/build/utils/convertPCMToFloat32.js +54 -0
- package/build/utils/convertPCMToFloat32.js.map +1 -0
- package/build/utils/encodingToBitDepth.d.ts +5 -0
- package/build/utils/encodingToBitDepth.d.ts.map +1 -0
- package/build/utils/encodingToBitDepth.js +13 -0
- package/build/utils/encodingToBitDepth.js.map +1 -0
- package/build/utils/getWavFileInfo.d.ts +26 -0
- package/build/utils/getWavFileInfo.d.ts.map +1 -0
- package/build/utils/getWavFileInfo.js +92 -0
- package/build/utils/getWavFileInfo.js.map +1 -0
- package/build/utils/writeWavHeader.d.ts +9 -0
- package/build/utils/writeWavHeader.d.ts.map +1 -0
- package/build/utils/writeWavHeader.js +41 -0
- package/build/utils/writeWavHeader.js.map +1 -0
- package/build/workers/InlineFeaturesExtractor.web.d.ts +2 -0
- package/build/workers/InlineFeaturesExtractor.web.d.ts.map +1 -0
- package/build/workers/InlineFeaturesExtractor.web.js +303 -0
- package/build/workers/InlineFeaturesExtractor.web.js.map +1 -0
- package/build/workers/inlineAudioWebWorker.web.d.ts +2 -0
- package/build/workers/inlineAudioWebWorker.web.d.ts.map +1 -0
- package/build/workers/inlineAudioWebWorker.web.js +243 -0
- package/build/workers/inlineAudioWebWorker.web.js.map +1 -0
- package/expo-module.config.json +8 -17
- package/ios/AudioStreamManager.swift +40 -2
- package/ios/ExpoAudioStreamModule.swift +11 -0
- package/ios/RecordingResult.swift +1 -0
- package/package.json +72 -64
- package/plugin/build/index.d.ts +1 -1
- package/plugin/build/index.js +7 -7
- package/plugin/src/index.ts +47 -47
- package/plugin/tsconfig.json +8 -13
- package/publish.sh +0 -0
- package/src/AudioAnalysis/AudioAnalysis.types.ts +84 -0
- package/src/AudioAnalysis/extractAudioAnalysis.ts +147 -0
- package/src/AudioAnalysis/extractWaveform.ts +25 -0
- package/src/AudioRecorder.provider.tsx +59 -31
- package/src/ExpoAudioStream.native.ts +2 -2
- package/src/ExpoAudioStream.types.ts +58 -116
- package/src/ExpoAudioStream.web.ts +233 -205
- package/src/ExpoAudioStreamModule.ts +18 -12
- package/src/WebRecorder.web.ts +433 -0
- package/src/constants.ts +18 -0
- package/src/events.ts +39 -0
- package/src/index.ts +15 -176
- package/src/logger.ts +23 -0
- package/src/useAudioRecorder.tsx +420 -0
- package/src/utils/BlobFix.ts +550 -0
- package/src/utils/concatenateBuffers.ts +24 -0
- package/src/utils/convertPCMToFloat32.ts +75 -0
- package/src/utils/encodingToBitDepth.ts +18 -0
- package/src/utils/getWavFileInfo.ts +132 -0
- package/src/utils/writeWavHeader.ts +56 -0
- package/src/workers/InlineFeaturesExtractor.web.tsx +302 -0
- package/src/workers/inlineAudioWebWorker.web.tsx +242 -0
- package/tsconfig.json +12 -7
- package/build/WebRecorder.d.ts.map +0 -1
- package/build/WebRecorder.js.map +0 -1
- package/build/inlineAudioWebWorker.d.ts +0 -3
- package/build/inlineAudioWebWorker.d.ts.map +0 -1
- package/build/inlineAudioWebWorker.js +0 -340
- package/build/inlineAudioWebWorker.js.map +0 -1
- package/build/useAudioRecording.d.ts +0 -38
- package/build/useAudioRecording.d.ts.map +0 -1
- package/build/useAudioRecording.js.map +0 -1
- package/build/utils.d.ts +0 -31
- package/build/utils.d.ts.map +0 -1
- package/build/utils.js +0 -143
- package/build/utils.js.map +0 -1
- package/src/WebRecorder.ts +0 -364
- package/src/inlineAudioWebWorker.tsx +0 -340
- package/src/useAudioRecording.tsx +0 -410
- package/src/utils.ts +0 -189
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAe;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAe;AAEf,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAA;AAC3E,OAAO,EACH,qBAAqB,EACrB,sBAAsB,GACzB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAErD,cAAc,wBAAwB,CAAA;AACtC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,wBAAwB,CAAA;AAEtC,OAAO,EACH,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,GACzB,CAAA","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
|
+
warn: (message: string, ...args: unknown[]) => void;
|
|
5
|
+
error: (message: string, ...args: unknown[]) => void;
|
|
6
|
+
};
|
|
7
|
+
export declare const getLogger: (tag: string) => ConsoleLike;
|
|
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;IACf,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;IAClD,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;IACpD,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;IACnD,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;CACvD,CAAA;AAED,eAAO,MAAM,SAAS,QAAS,MAAM,KAAG,WASvC,CAAA"}
|
package/build/logger.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// packages/expo-audio-stream/src/logger.ts
|
|
2
|
+
import { getLogger as siteedGetLogger } from '@siteed/react-native-logger';
|
|
3
|
+
import { DEBUG_NAMESPACE } from './constants';
|
|
4
|
+
export const getLogger = (tag) => {
|
|
5
|
+
const baseLogger = siteedGetLogger(`${DEBUG_NAMESPACE}:${tag}`);
|
|
6
|
+
return {
|
|
7
|
+
log: (...args) => baseLogger.log(...args),
|
|
8
|
+
debug: (...args) => baseLogger.debug(...args),
|
|
9
|
+
error: (...args) => baseLogger.error(...args),
|
|
10
|
+
warn: (...args) => baseLogger.warn(...args),
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types';
|
|
2
|
+
import { AudioRecording, RecordingConfig, StartRecordingResult } from './ExpoAudioStream.types';
|
|
3
|
+
export interface UseAudioRecorderProps {
|
|
4
|
+
debug?: boolean;
|
|
5
|
+
audioWorkletUrl?: string;
|
|
6
|
+
featuresExtratorUrl?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface UseAudioRecorderState {
|
|
9
|
+
startRecording: (_: RecordingConfig) => Promise<StartRecordingResult>;
|
|
10
|
+
stopRecording: () => Promise<AudioRecording>;
|
|
11
|
+
pauseRecording: () => void;
|
|
12
|
+
resumeRecording: () => void;
|
|
13
|
+
isRecording: boolean;
|
|
14
|
+
isPaused: boolean;
|
|
15
|
+
durationMs: number;
|
|
16
|
+
size: number;
|
|
17
|
+
analysisData?: AudioAnalysis;
|
|
18
|
+
}
|
|
19
|
+
export declare function useAudioRecorder({ debug, audioWorkletUrl, featuresExtratorUrl, }?: UseAudioRecorderProps): UseAudioRecorderState;
|
|
20
|
+
//# sourceMappingURL=useAudioRecorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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,24 +1,29 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import { Platform } from
|
|
3
|
-
import { useCallback, useEffect, useReducer, useRef } from
|
|
4
|
-
import
|
|
5
|
-
import
|
|
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 { getLogger } from './logger';
|
|
7
|
+
const TAG = 'useAudioRecorder';
|
|
8
|
+
const logger = getLogger(TAG);
|
|
6
9
|
const defaultAnalysis = {
|
|
7
|
-
pointsPerSecond:
|
|
10
|
+
pointsPerSecond: 10,
|
|
8
11
|
bitDepth: 32,
|
|
9
12
|
numberOfChannels: 1,
|
|
10
13
|
durationMs: 0,
|
|
11
14
|
sampleRate: 44100,
|
|
12
15
|
samples: 0,
|
|
13
16
|
dataPoints: [],
|
|
17
|
+
amplitudeAlgorithm: 'rms',
|
|
18
|
+
speakerChanges: [],
|
|
14
19
|
amplitudeRange: {
|
|
15
20
|
min: Number.POSITIVE_INFINITY,
|
|
16
21
|
max: Number.NEGATIVE_INFINITY,
|
|
17
22
|
},
|
|
18
23
|
};
|
|
19
|
-
function
|
|
24
|
+
function audioRecorderReducer(state, action) {
|
|
20
25
|
switch (action.type) {
|
|
21
|
-
case
|
|
26
|
+
case 'START':
|
|
22
27
|
return {
|
|
23
28
|
...state,
|
|
24
29
|
isRecording: true,
|
|
@@ -27,19 +32,19 @@ function recorderReducer(state, action) {
|
|
|
27
32
|
size: 0,
|
|
28
33
|
analysisData: defaultAnalysis, // Reset analysis data
|
|
29
34
|
};
|
|
30
|
-
case
|
|
35
|
+
case 'STOP':
|
|
31
36
|
return { ...state, isRecording: false, isPaused: false };
|
|
32
|
-
case
|
|
37
|
+
case 'PAUSE':
|
|
33
38
|
return { ...state, isPaused: true, isRecording: false };
|
|
34
|
-
case
|
|
39
|
+
case 'RESUME':
|
|
35
40
|
return { ...state, isPaused: false, isRecording: true };
|
|
36
|
-
case
|
|
41
|
+
case 'UPDATE_STATUS':
|
|
37
42
|
return {
|
|
38
43
|
...state,
|
|
39
44
|
durationMs: action.payload.durationMs,
|
|
40
45
|
size: action.payload.size,
|
|
41
46
|
};
|
|
42
|
-
case
|
|
47
|
+
case 'UPDATE_ANALYSIS':
|
|
43
48
|
return {
|
|
44
49
|
...state,
|
|
45
50
|
analysisData: action.payload,
|
|
@@ -48,9 +53,8 @@ function recorderReducer(state, action) {
|
|
|
48
53
|
return state;
|
|
49
54
|
}
|
|
50
55
|
}
|
|
51
|
-
const TAG = "[ useAudioRecorder ] ";
|
|
52
56
|
export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtratorUrl, } = {}) {
|
|
53
|
-
const [state, dispatch] = useReducer(
|
|
57
|
+
const [state, dispatch] = useReducer(audioRecorderReducer, {
|
|
54
58
|
isRecording: false,
|
|
55
59
|
isPaused: false,
|
|
56
60
|
durationMs: 0,
|
|
@@ -60,24 +64,16 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
|
|
|
60
64
|
const analysisListenerRef = useRef(null);
|
|
61
65
|
const analysisRef = useRef({ ...defaultAnalysis });
|
|
62
66
|
// Instantiate the module for web with URLs
|
|
63
|
-
const ExpoAudioStream = Platform.OS ===
|
|
67
|
+
const ExpoAudioStream = Platform.OS === 'web'
|
|
64
68
|
? ExpoAudioStreamModule({ audioWorkletUrl, featuresExtratorUrl })
|
|
65
69
|
: ExpoAudioStreamModule;
|
|
66
70
|
const onAudioStreamRef = useRef(null);
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
console.log(`${TAG} ${message}`);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}, [debug]);
|
|
77
|
-
const handleAudioAnalysis = useCallback(async (analysis, visualizationDuration) => {
|
|
78
|
-
const savedAnalysisData = analysisRef.current || { ...defaultAnalysis };
|
|
71
|
+
const handleAudioAnalysis = useCallback(async ({ analysis, visualizationDuration, }) => {
|
|
72
|
+
const savedAnalysisData = analysisRef.current || {
|
|
73
|
+
...defaultAnalysis,
|
|
74
|
+
};
|
|
79
75
|
const maxDuration = visualizationDuration;
|
|
80
|
-
|
|
76
|
+
logger.debug(`[handleAudioAnalysis] Received audio analysis: maxDuration=${maxDuration} analysis.dataPoints=${analysis.dataPoints.length} analysisData.dataPoints=${savedAnalysisData.dataPoints.length}`, analysis);
|
|
81
77
|
// Combine data points
|
|
82
78
|
const combinedDataPoints = [
|
|
83
79
|
...savedAnalysisData.dataPoints,
|
|
@@ -86,7 +82,7 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
|
|
|
86
82
|
// Calculate the new duration
|
|
87
83
|
const pointsPerSecond = analysis.pointsPerSecond || savedAnalysisData.pointsPerSecond;
|
|
88
84
|
const maxDataPoints = (pointsPerSecond * visualizationDuration) / 1000;
|
|
89
|
-
|
|
85
|
+
logger.debug(`[handleAudioAnalysis] Combined data points before trimming: pointsPerSecond=${pointsPerSecond} visualizationDuration=${visualizationDuration} combinedDataPointsLength=${combinedDataPoints.length} vs maxDataPoints=${maxDataPoints}`);
|
|
90
86
|
// Trim data points to keep within the maximum number of data points
|
|
91
87
|
if (combinedDataPoints.length > maxDataPoints) {
|
|
92
88
|
combinedDataPoints.splice(0, combinedDataPoints.length - maxDataPoints);
|
|
@@ -103,16 +99,19 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
|
|
|
103
99
|
min: newMin,
|
|
104
100
|
max: newMax,
|
|
105
101
|
};
|
|
106
|
-
|
|
102
|
+
logger.debug(`[handleAudioAnalysis] Updated analysis data: durationMs=${savedAnalysisData.durationMs}`, savedAnalysisData);
|
|
107
103
|
// Update the ref
|
|
108
104
|
analysisRef.current = savedAnalysisData;
|
|
109
105
|
// Dispatch the updated analysis data to state to trigger re-render
|
|
110
106
|
// need to use spread operator otherwise it doesnt trigger update.
|
|
111
|
-
dispatch({
|
|
112
|
-
|
|
107
|
+
dispatch({
|
|
108
|
+
type: 'UPDATE_ANALYSIS',
|
|
109
|
+
payload: { ...savedAnalysisData },
|
|
110
|
+
});
|
|
111
|
+
}, [dispatch]);
|
|
113
112
|
const handleAudioEvent = useCallback(async (eventData) => {
|
|
114
113
|
const { fileUri, deltaSize, totalSize, lastEmittedSize, position, streamUuid, encoded, mimeType, buffer, } = eventData;
|
|
115
|
-
|
|
114
|
+
logger.debug(`[handleAudioEvent] Received audio event:`, {
|
|
116
115
|
fileUri,
|
|
117
116
|
deltaSize,
|
|
118
117
|
totalSize,
|
|
@@ -128,11 +127,11 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
|
|
|
128
127
|
}
|
|
129
128
|
try {
|
|
130
129
|
// Coming from native ( ios / android ) otherwise buffer is set
|
|
131
|
-
if (Platform.OS !==
|
|
130
|
+
if (Platform.OS !== 'web') {
|
|
132
131
|
// Read the audio file as a base64 string for comparison
|
|
133
132
|
if (!encoded) {
|
|
134
133
|
console.error(`${TAG} Encoded audio data is missing`);
|
|
135
|
-
throw new Error(
|
|
134
|
+
throw new Error('Encoded audio data is missing');
|
|
136
135
|
}
|
|
137
136
|
onAudioStreamRef.current?.({
|
|
138
137
|
data: encoded,
|
|
@@ -144,68 +143,47 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
|
|
|
144
143
|
}
|
|
145
144
|
else if (buffer) {
|
|
146
145
|
// Coming from web
|
|
147
|
-
|
|
146
|
+
const webEvent = {
|
|
148
147
|
data: buffer,
|
|
149
148
|
position,
|
|
150
149
|
fileUri,
|
|
151
150
|
eventDataSize: deltaSize,
|
|
152
151
|
totalSize,
|
|
153
|
-
}
|
|
152
|
+
};
|
|
153
|
+
onAudioStreamRef.current?.(webEvent);
|
|
154
|
+
logger.debug(`[handleAudioEvent] Audio data sent to onAudioStream`, webEvent);
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
157
|
catch (error) {
|
|
157
158
|
console.error(`${TAG} Error processing audio event:`, error);
|
|
158
159
|
}
|
|
159
|
-
}, [
|
|
160
|
+
}, []);
|
|
160
161
|
const checkStatus = useCallback(async () => {
|
|
161
162
|
try {
|
|
162
163
|
if (!state.isRecording) {
|
|
163
|
-
|
|
164
|
+
logger.debug(`Not recording, exiting status check.`);
|
|
164
165
|
return;
|
|
165
166
|
}
|
|
166
167
|
const status = ExpoAudioStream.status();
|
|
167
168
|
if (debug) {
|
|
168
|
-
|
|
169
|
+
logger.debug(`Status:`, status);
|
|
169
170
|
}
|
|
170
171
|
dispatch({
|
|
171
|
-
type:
|
|
172
|
+
type: 'UPDATE_STATUS',
|
|
172
173
|
payload: { durationMs: status.durationMs, size: status.size },
|
|
173
174
|
});
|
|
174
175
|
}
|
|
175
176
|
catch (error) {
|
|
176
177
|
console.error(`${TAG} Error getting status:`, error);
|
|
177
178
|
}
|
|
178
|
-
}, [state.isRecording
|
|
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]);
|
|
179
|
+
}, [state.isRecording]);
|
|
201
180
|
const startRecording = useCallback(async (recordingOptions) => {
|
|
202
|
-
|
|
203
|
-
logDebug(`start recoding`, recordingOptions);
|
|
204
|
-
}
|
|
181
|
+
logger.debug(`start recoding`, recordingOptions);
|
|
205
182
|
analysisRef.current = { ...defaultAnalysis }; // Reset analysis data
|
|
206
183
|
const { onAudioStream, ...options } = recordingOptions;
|
|
207
|
-
const {
|
|
208
|
-
|
|
184
|
+
const { enableProcessing } = options;
|
|
185
|
+
const maxRecentDataDuration = 10000; // TODO compute maxRecentDataDuration based on screen dimensions
|
|
186
|
+
if (typeof onAudioStream === 'function') {
|
|
209
187
|
onAudioStreamRef.current = onAudioStream;
|
|
210
188
|
}
|
|
211
189
|
else {
|
|
@@ -213,12 +191,15 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
|
|
|
213
191
|
onAudioStreamRef.current = null;
|
|
214
192
|
}
|
|
215
193
|
const startResult = await ExpoAudioStream.startRecording(options);
|
|
216
|
-
dispatch({ type:
|
|
194
|
+
dispatch({ type: 'START' });
|
|
217
195
|
if (enableProcessing) {
|
|
218
|
-
|
|
196
|
+
logger.debug(`Enabling audio analysis listener`);
|
|
219
197
|
const listener = addAudioAnalysisListener(async (analysisData) => {
|
|
220
198
|
try {
|
|
221
|
-
await handleAudioAnalysis(
|
|
199
|
+
await handleAudioAnalysis({
|
|
200
|
+
analysis: analysisData,
|
|
201
|
+
visualizationDuration: maxRecentDataDuration,
|
|
202
|
+
});
|
|
222
203
|
}
|
|
223
204
|
catch (error) {
|
|
224
205
|
console.warn(`${TAG} Error processing audio analysis:`, error);
|
|
@@ -227,31 +208,54 @@ export function useAudioRecorder({ debug = false, audioWorkletUrl, featuresExtra
|
|
|
227
208
|
analysisListenerRef.current = listener;
|
|
228
209
|
}
|
|
229
210
|
return startResult;
|
|
230
|
-
}, [
|
|
211
|
+
}, [handleAudioAnalysis, dispatch]);
|
|
231
212
|
const stopRecording = useCallback(async () => {
|
|
232
|
-
|
|
213
|
+
logger.debug(`stoping recording`);
|
|
214
|
+
const stopResult = await ExpoAudioStream.stopRecording();
|
|
215
|
+
stopResult.analysisData = analysisRef.current;
|
|
233
216
|
if (analysisListenerRef.current) {
|
|
234
217
|
analysisListenerRef.current.remove();
|
|
235
218
|
analysisListenerRef.current = null;
|
|
236
219
|
}
|
|
237
|
-
const stopResult = await ExpoAudioStream.stopRecording();
|
|
238
220
|
onAudioStreamRef.current = null;
|
|
239
|
-
|
|
240
|
-
dispatch({ type:
|
|
221
|
+
logger.debug(`recording stopped`, stopResult);
|
|
222
|
+
dispatch({ type: 'STOP' });
|
|
241
223
|
return stopResult;
|
|
242
|
-
}, [
|
|
224
|
+
}, [dispatch]);
|
|
243
225
|
const pauseRecording = useCallback(async () => {
|
|
244
|
-
|
|
226
|
+
logger.debug(`pause recording`);
|
|
245
227
|
const pauseResult = await ExpoAudioStream.pauseRecording();
|
|
246
|
-
dispatch({ type:
|
|
228
|
+
dispatch({ type: 'PAUSE' });
|
|
247
229
|
return pauseResult;
|
|
248
|
-
}, [
|
|
230
|
+
}, [dispatch]);
|
|
249
231
|
const resumeRecording = useCallback(async () => {
|
|
250
|
-
|
|
232
|
+
logger.debug(`resume recording`);
|
|
251
233
|
const resumeResult = await ExpoAudioStream.resumeRecording();
|
|
252
|
-
dispatch({ type:
|
|
234
|
+
dispatch({ type: 'RESUME' });
|
|
253
235
|
return resumeResult;
|
|
254
|
-
}, [
|
|
236
|
+
}, [dispatch]);
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
let interval;
|
|
239
|
+
if (state.isRecording) {
|
|
240
|
+
interval = setInterval(checkStatus, 1000);
|
|
241
|
+
}
|
|
242
|
+
return () => {
|
|
243
|
+
if (interval) {
|
|
244
|
+
clearInterval(interval);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}, [checkStatus, state.isRecording]);
|
|
248
|
+
useEffect(() => {
|
|
249
|
+
logger.debug(`Registering audio event listener`);
|
|
250
|
+
const subscribeAudio = addAudioEventListener(handleAudioEvent);
|
|
251
|
+
logger.debug(`Subscribed to audio event listener and analysis listener`, {
|
|
252
|
+
subscribeAudio,
|
|
253
|
+
});
|
|
254
|
+
return () => {
|
|
255
|
+
logger.debug(`Removing audio event listener`);
|
|
256
|
+
subscribeAudio.remove();
|
|
257
|
+
};
|
|
258
|
+
}, [handleAudioEvent, handleAudioAnalysis]);
|
|
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=
|
|
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 { 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"}
|