@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.
- package/.size-limit.json +4 -4
- 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 +3 -5
- package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -1
- package/build/AudioAnalysis/AudioAnalysis.types.js.map +1 -1
- package/build/AudioAnalysis/extractAudioAnalysis.d.ts +19 -3
- package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -1
- package/build/AudioAnalysis/extractAudioAnalysis.js +14 -27
- package/build/AudioAnalysis/extractAudioAnalysis.js.map +1 -1
- package/build/AudioAnalysis/extractWaveform.d.ts.map +1 -1
- package/build/AudioAnalysis/extractWaveform.js +3 -3
- package/build/AudioAnalysis/extractWaveform.js.map +1 -1
- package/build/AudioRecorder.provider.d.ts +6 -6
- package/build/AudioRecorder.provider.d.ts.map +1 -1
- package/build/AudioRecorder.provider.js +9 -9
- 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 +20 -18
- package/build/ExpoAudioStream.types.d.ts.map +1 -1
- package/build/ExpoAudioStream.types.js.map +1 -1
- package/build/ExpoAudioStream.web.d.ts +8 -8
- package/build/ExpoAudioStream.web.d.ts.map +1 -1
- package/build/ExpoAudioStream.web.js +40 -22
- package/build/ExpoAudioStream.web.js.map +1 -1
- package/build/ExpoAudioStreamModule.d.ts.map +1 -1
- package/build/ExpoAudioStreamModule.js +8 -7
- package/build/ExpoAudioStreamModule.js.map +1 -1
- package/build/WebRecorder.web.d.ts +8 -8
- package/build/WebRecorder.web.d.ts.map +1 -1
- package/build/WebRecorder.web.js +60 -50
- package/build/WebRecorder.web.js.map +1 -1
- package/build/constants.d.ts +1 -1
- package/build/constants.d.ts.map +1 -1
- package/build/constants.js +3 -3
- package/build/constants.js.map +1 -1
- package/build/events.d.ts +16 -4
- package/build/events.d.ts.map +1 -1
- package/build/events.js +8 -8
- package/build/events.js.map +1 -1
- package/build/index.d.ts +8 -8
- package/build/index.d.ts.map +1 -1
- package/build/index.js +6 -6
- package/build/index.js.map +1 -1
- package/build/logger.d.ts +2 -2
- package/build/logger.d.ts.map +1 -1
- package/build/logger.js +7 -11
- package/build/logger.js.map +1 -1
- package/build/useAudioRecorder.d.ts +4 -21
- package/build/useAudioRecorder.d.ts.map +1 -1
- package/build/useAudioRecorder.js +33 -33
- package/build/useAudioRecorder.js.map +1 -1
- 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 +2 -2
- package/build/utils/convertPCMToFloat32.d.ts.map +1 -1
- package/build/utils/convertPCMToFloat32.js +49 -36
- package/build/utils/convertPCMToFloat32.js.map +1 -1
- package/build/utils/encodingToBitDepth.d.ts +1 -1
- package/build/utils/encodingToBitDepth.d.ts.map +1 -1
- package/build/utils/encodingToBitDepth.js +3 -3
- package/build/utils/encodingToBitDepth.js.map +1 -1
- package/build/utils/getWavFileInfo.d.ts +4 -3
- package/build/utils/getWavFileInfo.d.ts.map +1 -1
- package/build/utils/getWavFileInfo.js +18 -15
- package/build/utils/getWavFileInfo.js.map +1 -1
- package/build/utils/writeWavHeader.d.ts.map +1 -1
- package/build/utils/writeWavHeader.js +4 -4
- package/build/utils/writeWavHeader.js.map +1 -1
- package/build/workers/InlineFeaturesExtractor.web.d.ts.map +1 -1
- package/build/workers/InlineFeaturesExtractor.web.js.map +1 -1
- package/build/workers/inlineAudioWebWorker.web.d.ts.map +1 -1
- package/build/workers/inlineAudioWebWorker.web.js.map +1 -1
- package/expo-module.config.json +8 -17
- package/ios/AudioStreamManager.swift +1 -0
- package/ios/ExpoAudioStreamModule.swift +1 -0
- package/ios/RecordingResult.swift +1 -0
- package/package.json +72 -65
- 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/src/AudioAnalysis/AudioAnalysis.types.ts +59 -60
- package/src/AudioAnalysis/extractAudioAnalysis.ts +132 -121
- package/src/AudioAnalysis/extractWaveform.ts +18 -18
- package/src/AudioRecorder.provider.tsx +53 -53
- package/src/ExpoAudioStream.native.ts +2 -2
- package/src/ExpoAudioStream.types.ts +56 -53
- package/src/ExpoAudioStream.web.ts +232 -205
- package/src/ExpoAudioStreamModule.ts +17 -16
- package/src/WebRecorder.web.ts +407 -390
- package/src/constants.ts +11 -11
- package/src/events.ts +27 -13
- package/src/index.ts +15 -15
- package/src/logger.ts +15 -18
- package/src/useAudioRecorder.tsx +394 -389
- package/src/utils/BlobFix.ts +550 -0
- package/src/utils/concatenateBuffers.ts +24 -0
- package/src/utils/convertPCMToFloat32.ts +72 -45
- package/src/utils/encodingToBitDepth.ts +14 -14
- package/src/utils/getWavFileInfo.ts +106 -99
- package/src/utils/writeWavHeader.ts +45 -45
- package/src/workers/InlineFeaturesExtractor.web.tsx +1 -1
- package/src/workers/inlineAudioWebWorker.web.tsx +1 -1
- package/tsconfig.json +12 -7
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AmplitudeAlgorithm, AudioAnalysis, AudioFeaturesOptions } from './AudioAnalysis/AudioAnalysis.types';
|
|
2
|
+
import { AudioAnalysisEvent } from './events';
|
|
2
3
|
export interface AudioStreamStatus {
|
|
3
4
|
isRecording: boolean;
|
|
4
5
|
isPaused: boolean;
|
|
@@ -8,35 +9,36 @@ export interface AudioStreamStatus {
|
|
|
8
9
|
mimeType: string;
|
|
9
10
|
}
|
|
10
11
|
export interface AudioDataEvent {
|
|
11
|
-
data: string |
|
|
12
|
+
data: string | Float32Array;
|
|
12
13
|
position: number;
|
|
13
14
|
fileUri: string;
|
|
14
15
|
eventDataSize: number;
|
|
15
16
|
totalSize: number;
|
|
16
17
|
}
|
|
17
|
-
export
|
|
18
|
-
encoded?: string;
|
|
19
|
-
buffer?: ArrayBuffer;
|
|
20
|
-
fileUri: string;
|
|
21
|
-
lastEmittedSize: number;
|
|
22
|
-
position: number;
|
|
23
|
-
deltaSize: number;
|
|
24
|
-
totalSize: number;
|
|
25
|
-
mimeType: string;
|
|
26
|
-
streamUuid: string;
|
|
27
|
-
}
|
|
28
|
-
export type EncodingType = "pcm_32bit" | "pcm_16bit" | "pcm_8bit";
|
|
18
|
+
export type EncodingType = 'pcm_32bit' | 'pcm_16bit' | 'pcm_8bit';
|
|
29
19
|
export type SampleRate = 16000 | 44100 | 48000;
|
|
30
20
|
export type BitDepth = 8 | 16 | 32;
|
|
31
|
-
export interface
|
|
21
|
+
export interface Chunk {
|
|
22
|
+
text: string;
|
|
23
|
+
timestamp: [number, number | null];
|
|
24
|
+
}
|
|
25
|
+
export interface TranscriberData {
|
|
26
|
+
isBusy: boolean;
|
|
27
|
+
text: string;
|
|
28
|
+
chunks: Chunk[];
|
|
29
|
+
}
|
|
30
|
+
export interface AudioRecording {
|
|
32
31
|
fileUri: string;
|
|
33
|
-
|
|
32
|
+
filename: string;
|
|
34
33
|
durationMs: number;
|
|
35
34
|
size: number;
|
|
36
35
|
mimeType: string;
|
|
37
36
|
channels: number;
|
|
38
37
|
bitDepth: BitDepth;
|
|
39
38
|
sampleRate: SampleRate;
|
|
39
|
+
transcripts?: TranscriberData[];
|
|
40
|
+
wavPCMData?: Float32Array;
|
|
41
|
+
analysisData?: AudioAnalysis;
|
|
40
42
|
}
|
|
41
43
|
export interface StartRecordingResult {
|
|
42
44
|
fileUri: string;
|
|
@@ -52,9 +54,9 @@ export interface RecordingConfig {
|
|
|
52
54
|
interval?: number;
|
|
53
55
|
enableProcessing?: boolean;
|
|
54
56
|
pointsPerSecond?: number;
|
|
55
|
-
algorithm?:
|
|
57
|
+
algorithm?: AmplitudeAlgorithm;
|
|
56
58
|
features?: AudioFeaturesOptions;
|
|
57
59
|
onAudioStream?: (_: AudioDataEvent) => Promise<void>;
|
|
58
|
-
|
|
60
|
+
onAudioAnalysis?: (_: AudioAnalysisEvent) => Promise<void>;
|
|
59
61
|
}
|
|
60
62
|
//# sourceMappingURL=ExpoAudioStream.types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAudioStream.types.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStream.types.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"ExpoAudioStream.types.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStream.types.ts"],"names":[],"mappings":"AACA,OAAO,EACH,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACvB,MAAM,qCAAqC,CAAA;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAE7C,MAAM,WAAW,iBAAiB;IAC9B,WAAW,EAAE,OAAO,CAAA;IACpB,QAAQ,EAAE,OAAO,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,GAAG,YAAY,CAAA;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,CAAA;AACjE,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAA;AAC9C,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,CAAA;AAElC,MAAM,WAAW,KAAK;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAA;CACrC;AAED,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAE,OAAO,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,KAAK,EAAE,CAAA;CAClB;AAED,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,QAAQ,CAAA;IAClB,UAAU,EAAE,UAAU,CAAA;IACtB,WAAW,CAAC,EAAE,eAAe,EAAE,CAAA;IAC/B,UAAU,CAAC,EAAE,YAAY,CAAA;IACzB,YAAY,CAAC,EAAE,aAAa,CAAA;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;CAC1B;AAED,MAAM,WAAW,eAAe;IAC5B,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;IAChB,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IAGjB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,SAAS,CAAC,EAAE,kBAAkB,CAAA;IAC9B,QAAQ,CAAC,EAAE,oBAAoB,CAAA;IAE/B,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpD,eAAe,CAAC,EAAE,CAAC,CAAC,EAAE,kBAAkB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAudioStream.types.js","sourceRoot":"","sources":["../src/ExpoAudioStream.types.ts"],"names":[],"mappings":"","sourcesContent":["// packages/expo-audio-stream/src/ExpoAudioStream.types.ts\nimport {\n
|
|
1
|
+
{"version":3,"file":"ExpoAudioStream.types.js","sourceRoot":"","sources":["../src/ExpoAudioStream.types.ts"],"names":[],"mappings":"","sourcesContent":["// packages/expo-audio-stream/src/ExpoAudioStream.types.ts\nimport {\n AmplitudeAlgorithm,\n AudioAnalysis,\n AudioFeaturesOptions,\n} from './AudioAnalysis/AudioAnalysis.types'\nimport { AudioAnalysisEvent } from './events'\n\nexport interface AudioStreamStatus {\n isRecording: boolean\n isPaused: boolean\n durationMs: number\n size: number\n interval: number\n mimeType: string\n}\n\nexport interface AudioDataEvent {\n data: string | Float32Array\n position: number\n fileUri: string\n eventDataSize: number\n totalSize: number\n}\n\nexport type EncodingType = 'pcm_32bit' | 'pcm_16bit' | 'pcm_8bit'\nexport type SampleRate = 16000 | 44100 | 48000\nexport type BitDepth = 8 | 16 | 32\n\nexport interface Chunk {\n text: string\n timestamp: [number, number | null]\n}\n\nexport interface TranscriberData {\n isBusy: boolean\n text: string\n chunks: Chunk[]\n}\n\nexport interface AudioRecording {\n fileUri: string\n filename: string\n durationMs: number\n size: number\n mimeType: string\n channels: number\n bitDepth: BitDepth\n sampleRate: SampleRate\n transcripts?: TranscriberData[]\n wavPCMData?: Float32Array // Full PCM data for the recording in WAV format (only on web, for native use the fileUri)\n analysisData?: AudioAnalysis // Analysis data for the recording depending on enableProcessing flag\n}\n\nexport interface StartRecordingResult {\n fileUri: string\n mimeType: string\n channels?: number\n bitDepth?: BitDepth\n sampleRate?: SampleRate\n}\n\nexport interface RecordingConfig {\n sampleRate?: SampleRate // Sample rate for recording\n channels?: 1 | 2 // 1 or 2 (MONO or STEREO)\n encoding?: EncodingType // Encoding type for the recording\n interval?: number // Interval in milliseconds at which to emit recording data\n\n // Optional parameters for audio processing\n enableProcessing?: boolean // Boolean to enable/disable audio processing (default is false)\n pointsPerSecond?: number // Number of data points to extract per second of audio (default is 1000)\n algorithm?: AmplitudeAlgorithm // Algorithm to use for amplitude computation (default is \"rms\")\n features?: AudioFeaturesOptions // Feature options to extract (default is empty)\n\n onAudioStream?: (_: AudioDataEvent) => Promise<void> // Callback function to handle audio stream\n onAudioAnalysis?: (_: AudioAnalysisEvent) => Promise<void> // Callback function to handle audio features extraction results\n}\n"]}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { EventEmitter } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { WebRecorder } from
|
|
1
|
+
import { EventEmitter } from 'expo-modules-core';
|
|
2
|
+
import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types';
|
|
3
|
+
import { AudioRecording, AudioStreamStatus, BitDepth, RecordingConfig, StartRecordingResult } from './ExpoAudioStream.types';
|
|
4
|
+
import { WebRecorder } from './WebRecorder.web';
|
|
5
5
|
export interface EmitAudioEventProps {
|
|
6
|
-
data:
|
|
6
|
+
data: Float32Array;
|
|
7
7
|
position: number;
|
|
8
8
|
}
|
|
9
9
|
export type EmitAudioEventFunction = (_: EmitAudioEventProps) => void;
|
|
10
|
-
export type EmitAudioAnalysisFunction = (_:
|
|
10
|
+
export type EmitAudioAnalysisFunction = (_: AudioAnalysis) => void;
|
|
11
11
|
export interface ExpoAudioStreamWebProps {
|
|
12
12
|
audioWorkletUrl: string;
|
|
13
13
|
featuresExtratorUrl: string;
|
|
@@ -25,7 +25,7 @@ export declare class ExpoAudioStreamWeb extends EventEmitter {
|
|
|
25
25
|
lastEmittedSize: number;
|
|
26
26
|
lastEmittedTime: number;
|
|
27
27
|
streamUuid: string | null;
|
|
28
|
-
extension:
|
|
28
|
+
extension: 'webm' | 'wav';
|
|
29
29
|
recordingConfig?: RecordingConfig;
|
|
30
30
|
bitDepth: BitDepth;
|
|
31
31
|
audioWorkletUrl: string;
|
|
@@ -34,7 +34,7 @@ export declare class ExpoAudioStreamWeb extends EventEmitter {
|
|
|
34
34
|
getMediaStream(): Promise<MediaStream>;
|
|
35
35
|
startRecording(recordingConfig?: RecordingConfig): Promise<StartRecordingResult>;
|
|
36
36
|
emitAudioEvent({ data, position }: EmitAudioEventProps): void;
|
|
37
|
-
stopRecording(): Promise<
|
|
37
|
+
stopRecording(): Promise<AudioRecording>;
|
|
38
38
|
pauseRecording(): Promise<void>;
|
|
39
39
|
resumeRecording(): Promise<void>;
|
|
40
40
|
status(): AudioStreamStatus;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAudioStream.web.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStream.web.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"ExpoAudioStream.web.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStream.web.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AACnE,OAAO,EACH,cAAc,EACd,iBAAiB,EACjB,QAAQ,EACR,eAAe,EACf,oBAAoB,EACvB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAO/C,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,YAAY,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACnB;AACD,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,EAAE,mBAAmB,KAAK,IAAI,CAAA;AACrE,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,CAAA;AAElE,MAAM,WAAW,uBAAuB;IACpC,eAAe,EAAE,MAAM,CAAA;IACvB,mBAAmB,EAAE,MAAM,CAAA;CAC9B;AAID,qBAAa,kBAAmB,SAAQ,YAAY;IAChD,cAAc,EAAE,WAAW,GAAG,IAAI,CAAA;IAClC,WAAW,EAAE,WAAW,EAAE,CAAA;IAC1B,WAAW,EAAE,OAAO,CAAA;IACpB,QAAQ,EAAE,OAAO,CAAA;IACjB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,iBAAiB,EAAE,MAAM,CAAA;IACzB,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,SAAS,EAAE,MAAM,GAAG,KAAK,CAAQ;IACjC,eAAe,CAAC,EAAE,eAAe,CAAA;IACjC,QAAQ,EAAE,QAAQ,CAAA;IAClB,eAAe,EAAE,MAAM,CAAA;IACvB,mBAAmB,EAAE,MAAM,CAAA;gBAEf,EACR,eAAe,EACf,mBAAmB,GACtB,EAAE,uBAAuB;IA6BpB,cAAc;IAUd,cAAc,CAAC,eAAe,GAAE,eAAoB;IAiE1D,cAAc,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,mBAAmB;IAiBhD,aAAa,IAAI,OAAO,CAAC,cAAc,CAAC;IA2CxC,cAAc;IAad,eAAe;IAarB,MAAM;CAWT"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// src/ExpoAudioStreamModule.web.ts
|
|
2
|
-
import { EventEmitter } from
|
|
3
|
-
import { WebRecorder } from
|
|
4
|
-
import { getLogger } from
|
|
5
|
-
import { encodingToBitDepth } from
|
|
6
|
-
|
|
2
|
+
import { EventEmitter } from 'expo-modules-core';
|
|
3
|
+
import { WebRecorder } from './WebRecorder.web';
|
|
4
|
+
import { getLogger } from './logger';
|
|
5
|
+
import { encodingToBitDepth } from './utils/encodingToBitDepth';
|
|
6
|
+
import { writeWavHeader } from './utils/writeWavHeader';
|
|
7
|
+
const logger = getLogger('ExpoAudioStreamWeb');
|
|
7
8
|
export class ExpoAudioStreamWeb extends EventEmitter {
|
|
8
9
|
customRecorder;
|
|
9
10
|
audioChunks;
|
|
@@ -17,17 +18,17 @@ export class ExpoAudioStreamWeb extends EventEmitter {
|
|
|
17
18
|
lastEmittedSize;
|
|
18
19
|
lastEmittedTime;
|
|
19
20
|
streamUuid;
|
|
20
|
-
extension =
|
|
21
|
+
extension = 'wav'; // Default extension is 'webm'
|
|
21
22
|
recordingConfig;
|
|
22
23
|
bitDepth; // Bit depth of the audio
|
|
23
24
|
audioWorkletUrl;
|
|
24
25
|
featuresExtratorUrl;
|
|
25
26
|
constructor({ audioWorkletUrl, featuresExtratorUrl, }) {
|
|
26
27
|
const mockNativeModule = {
|
|
27
|
-
addListener: (
|
|
28
|
+
addListener: () => {
|
|
28
29
|
// Not used on web
|
|
29
30
|
},
|
|
30
|
-
removeListeners: (
|
|
31
|
+
removeListeners: () => {
|
|
31
32
|
// Not used on web
|
|
32
33
|
},
|
|
33
34
|
};
|
|
@@ -54,19 +55,20 @@ export class ExpoAudioStreamWeb extends EventEmitter {
|
|
|
54
55
|
return await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
55
56
|
}
|
|
56
57
|
catch (error) {
|
|
57
|
-
console.error(
|
|
58
|
+
console.error('Failed to get media stream:', error);
|
|
58
59
|
throw error;
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
// Start recording with options
|
|
62
63
|
async startRecording(recordingConfig = {}) {
|
|
63
64
|
if (this.isRecording) {
|
|
64
|
-
throw new Error(
|
|
65
|
+
throw new Error('Recording is already in progress');
|
|
65
66
|
}
|
|
66
67
|
this.bitDepth = encodingToBitDepth({
|
|
67
|
-
encoding: recordingConfig.encoding ??
|
|
68
|
+
encoding: recordingConfig.encoding ?? 'pcm_32bit',
|
|
68
69
|
});
|
|
69
70
|
const audioContext = new (window.AudioContext ||
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
70
72
|
// @ts-ignore - Allow webkitAudioContext for Safari
|
|
71
73
|
window.webkitAudioContext)();
|
|
72
74
|
const stream = await this.getMediaStream();
|
|
@@ -76,8 +78,7 @@ export class ExpoAudioStreamWeb extends EventEmitter {
|
|
|
76
78
|
source,
|
|
77
79
|
recordingConfig,
|
|
78
80
|
audioWorkletUrl: this.audioWorkletUrl,
|
|
79
|
-
|
|
80
|
-
emitAudioEventCallback: ({ data, position }) => {
|
|
81
|
+
emitAudioEventCallback: ({ data, position, }) => {
|
|
81
82
|
this.audioChunks.push(data);
|
|
82
83
|
this.currentSize += data.byteLength;
|
|
83
84
|
this.emitAudioEvent({ data, position });
|
|
@@ -86,7 +87,7 @@ export class ExpoAudioStreamWeb extends EventEmitter {
|
|
|
86
87
|
},
|
|
87
88
|
emitAudioAnalysisCallback: (audioAnalysisData) => {
|
|
88
89
|
logger.log(`Emitted AudioAnalysis:`, audioAnalysisData);
|
|
89
|
-
this.emit(
|
|
90
|
+
this.emit('AudioAnalysis', audioAnalysisData);
|
|
90
91
|
},
|
|
91
92
|
});
|
|
92
93
|
await this.customRecorder.init();
|
|
@@ -124,20 +125,37 @@ export class ExpoAudioStreamWeb extends EventEmitter {
|
|
|
124
125
|
position,
|
|
125
126
|
totalSize: this.currentSize,
|
|
126
127
|
buffer: data,
|
|
127
|
-
streamUuid: this.streamUuid ??
|
|
128
|
+
streamUuid: this.streamUuid ?? '', // Generate or manage UUID for stream identification
|
|
128
129
|
};
|
|
129
|
-
this.emit(
|
|
130
|
+
this.emit('AudioData', audioEventPayload);
|
|
130
131
|
}
|
|
131
132
|
// Stop recording
|
|
132
133
|
async stopRecording() {
|
|
133
|
-
if (this.customRecorder) {
|
|
134
|
-
|
|
135
|
-
logger.debug(`Stopped recording`, fullPcmBuffer);
|
|
134
|
+
if (!this.customRecorder) {
|
|
135
|
+
throw new Error('Recorder is not initialized');
|
|
136
136
|
}
|
|
137
|
+
const fullPcmBufferArray = await this.customRecorder.stop();
|
|
138
|
+
// concat all audio chunks
|
|
139
|
+
logger.debug(`Stopped recording`, fullPcmBufferArray);
|
|
137
140
|
this.isRecording = false;
|
|
138
141
|
this.currentDurationMs = Date.now() - this.recordingStartTime;
|
|
142
|
+
const wavConfig = {
|
|
143
|
+
buffer: fullPcmBufferArray.buffer,
|
|
144
|
+
sampleRate: this.recordingConfig?.sampleRate ?? 44100,
|
|
145
|
+
numChannels: this.recordingConfig?.channels ?? 1,
|
|
146
|
+
bitDepth: this.bitDepth,
|
|
147
|
+
};
|
|
148
|
+
logger.debug(`Writing wav header`, wavConfig);
|
|
149
|
+
const wavBuffer = writeWavHeader(wavConfig).slice(0);
|
|
150
|
+
// Create blob fileUri from audio chunks
|
|
151
|
+
const blob = new Blob([wavBuffer], {
|
|
152
|
+
type: `audio/${this.extension}`,
|
|
153
|
+
});
|
|
154
|
+
const fileUri = URL.createObjectURL(blob);
|
|
139
155
|
const result = {
|
|
140
|
-
fileUri
|
|
156
|
+
fileUri,
|
|
157
|
+
filename: `${this.streamUuid}.${this.extension}`,
|
|
158
|
+
wavPCMData: fullPcmBufferArray,
|
|
141
159
|
bitDepth: this.bitDepth,
|
|
142
160
|
channels: this.recordingConfig?.channels ?? 1,
|
|
143
161
|
sampleRate: this.recordingConfig?.sampleRate ?? 44100,
|
|
@@ -150,7 +168,7 @@ export class ExpoAudioStreamWeb extends EventEmitter {
|
|
|
150
168
|
// Pause recording
|
|
151
169
|
async pauseRecording() {
|
|
152
170
|
if (!this.isRecording || this.isPaused) {
|
|
153
|
-
throw new Error(
|
|
171
|
+
throw new Error('Recording is not active or already paused');
|
|
154
172
|
}
|
|
155
173
|
if (this.customRecorder) {
|
|
156
174
|
this.customRecorder.pause();
|
|
@@ -161,7 +179,7 @@ export class ExpoAudioStreamWeb extends EventEmitter {
|
|
|
161
179
|
// Resume recording
|
|
162
180
|
async resumeRecording() {
|
|
163
181
|
if (!this.isPaused) {
|
|
164
|
-
throw new Error(
|
|
182
|
+
throw new Error('Recording is not paused');
|
|
165
183
|
}
|
|
166
184
|
if (this.customRecorder) {
|
|
167
185
|
this.customRecorder.resume();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAudioStream.web.js","sourceRoot":"","sources":["../src/ExpoAudioStream.web.ts"],"names":[],"mappings":"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","sourcesContent":["// src/ExpoAudioStreamModule.web.ts\nimport { EventEmitter } from \"expo-modules-core\";\n\nimport { AudioAnalysisData } from \"./AudioAnalysis/AudioAnalysis.types\";\nimport {\n AudioEventPayload,\n AudioRecordingResult,\n AudioStreamStatus,\n BitDepth,\n RecordingConfig,\n StartRecordingResult,\n} from \"./ExpoAudioStream.types\";\nimport { WebRecorder } from \"./WebRecorder.web\";\nimport { getLogger } from \"./logger\";\nimport { encodingToBitDepth } from \"./utils/encodingToBitDepth\";\n\nexport interface EmitAudioEventProps {\n data: ArrayBuffer;\n position: number;\n}\nexport type EmitAudioEventFunction = (_: EmitAudioEventProps) => void;\nexport type EmitAudioAnalysisFunction = (_: AudioAnalysisData) => void;\n\nexport interface ExpoAudioStreamWebProps {\n audioWorkletUrl: string;\n featuresExtratorUrl: string;\n}\n\nconst logger = getLogger(\"ExpoAudioStreamWeb\");\n\nexport class ExpoAudioStreamWeb extends EventEmitter {\n customRecorder: WebRecorder | null;\n audioChunks: ArrayBuffer[];\n isRecording: boolean;\n isPaused: boolean;\n recordingStartTime: number;\n pausedTime: number;\n currentDurationMs: number;\n currentSize: number;\n currentInterval: number;\n lastEmittedSize: number;\n lastEmittedTime: number;\n streamUuid: string | null;\n extension: \"webm\" | \"wav\" = \"wav\"; // Default extension is 'webm'\n recordingConfig?: RecordingConfig;\n bitDepth: BitDepth; // Bit depth of the audio\n audioWorkletUrl: string;\n featuresExtratorUrl: string;\n\n constructor({\n audioWorkletUrl,\n featuresExtratorUrl,\n }: ExpoAudioStreamWebProps) {\n const mockNativeModule = {\n addListener: (eventName: string) => {\n // Not used on web\n },\n removeListeners: (count: number) => {\n // Not used on web\n },\n };\n super(mockNativeModule); // Pass the mock native module to the parent class\n\n this.customRecorder = null;\n this.audioChunks = [];\n this.isRecording = false;\n this.isPaused = false;\n this.recordingStartTime = 0;\n this.pausedTime = 0;\n this.currentDurationMs = 0;\n this.currentSize = 0;\n this.bitDepth = 32; // Default\n this.currentInterval = 1000; // Default interval in ms\n this.lastEmittedSize = 0;\n this.lastEmittedTime = 0;\n this.streamUuid = null; // Initialize UUID on first recording start\n this.audioWorkletUrl = audioWorkletUrl;\n this.featuresExtratorUrl = featuresExtratorUrl;\n }\n\n // Utility to handle user media stream\n async getMediaStream() {\n try {\n return await navigator.mediaDevices.getUserMedia({ audio: true });\n } catch (error) {\n console.error(\"Failed to get media stream:\", error);\n throw error;\n }\n }\n\n // Start recording with options\n async startRecording(recordingConfig: RecordingConfig = {}) {\n if (this.isRecording) {\n throw new Error(\"Recording is already in progress\");\n }\n\n this.bitDepth = encodingToBitDepth({\n encoding: recordingConfig.encoding ?? \"pcm_32bit\",\n });\n\n const audioContext = new (window.AudioContext ||\n // @ts-ignore - Allow webkitAudioContext for Safari\n window.webkitAudioContext)();\n const stream = await this.getMediaStream();\n\n const source = audioContext.createMediaStreamSource(stream);\n\n this.customRecorder = new WebRecorder({\n audioContext,\n source,\n recordingConfig,\n audioWorkletUrl: this.audioWorkletUrl,\n featuresExtratorUrl: this.featuresExtratorUrl,\n emitAudioEventCallback: ({ data, position }: EmitAudioEventProps) => {\n this.audioChunks.push(data);\n this.currentSize += data.byteLength;\n this.emitAudioEvent({ data, position });\n this.lastEmittedTime = Date.now();\n this.lastEmittedSize = this.currentSize;\n },\n emitAudioAnalysisCallback: (audioAnalysisData: AudioAnalysisData) => {\n logger.log(`Emitted AudioAnalysis:`, audioAnalysisData);\n this.emit(\"AudioAnalysis\", audioAnalysisData);\n },\n });\n await this.customRecorder.init();\n this.customRecorder.start();\n\n // // Set a timer to stop recording after 5 seconds\n // setTimeout(() => {\n // logger.log(\"AUTO Stopping recording\");\n // this.customRecorder?.stopAndPlay();\n // this.isRecording = false;\n // }, 3000);\n\n this.isRecording = true;\n this.recordingConfig = recordingConfig;\n this.recordingStartTime = Date.now();\n this.pausedTime = 0;\n this.lastEmittedSize = 0;\n this.lastEmittedTime = 0;\n this.streamUuid = Date.now().toString();\n const fileUri = `${this.streamUuid}.${this.extension}`;\n const streamConfig: StartRecordingResult = {\n fileUri,\n mimeType: `audio/${this.extension}`,\n bitDepth: this.bitDepth,\n channels: recordingConfig.channels ?? 1,\n sampleRate: recordingConfig.sampleRate ?? 44100,\n };\n return streamConfig;\n }\n\n emitAudioEvent({ data, position }: EmitAudioEventProps) {\n const fileUri = `${this.streamUuid}.${this.extension}`;\n const audioEventPayload: AudioEventPayload = {\n fileUri,\n mimeType: `audio/${this.extension}`,\n lastEmittedSize: this.lastEmittedSize, // Since this might be continuously streaming, adjust accordingly\n deltaSize: data.byteLength,\n position,\n totalSize: this.currentSize,\n buffer: data,\n streamUuid: this.streamUuid ?? \"\", // Generate or manage UUID for stream identification\n };\n\n this.emit(\"AudioData\", audioEventPayload);\n }\n\n // Stop recording\n async stopRecording(): Promise<AudioRecordingResult | null> {\n if (this.customRecorder) {\n const fullPcmBuffer = await this.customRecorder.stop();\n logger.debug(`Stopped recording`, fullPcmBuffer);\n }\n this.isRecording = false;\n this.currentDurationMs = Date.now() - this.recordingStartTime;\n const result: AudioRecordingResult = {\n fileUri: `${this.streamUuid}.${this.extension}`,\n bitDepth: this.bitDepth,\n channels: this.recordingConfig?.channels ?? 1,\n sampleRate: this.recordingConfig?.sampleRate ?? 44100,\n durationMs: this.currentDurationMs,\n size: this.currentSize,\n mimeType: `audio/${this.extension}`,\n };\n\n return result;\n }\n\n // Pause recording\n async pauseRecording() {\n if (!this.isRecording || this.isPaused) {\n throw new Error(\"Recording is not active or already paused\");\n }\n\n if (this.customRecorder) {\n this.customRecorder.pause();\n }\n this.isPaused = true;\n this.pausedTime = Date.now();\n }\n\n // Resume recording\n async resumeRecording() {\n if (!this.isPaused) {\n throw new Error(\"Recording is not paused\");\n }\n\n if (this.customRecorder) {\n this.customRecorder.resume();\n }\n this.isPaused = false;\n this.recordingStartTime += Date.now() - this.pausedTime;\n }\n\n // Get current status\n status() {\n const status: AudioStreamStatus = {\n isRecording: this.isRecording,\n isPaused: this.isPaused,\n durationMs: Date.now() - this.recordingStartTime,\n size: this.currentSize,\n interval: this.currentInterval,\n mimeType: `audio/${this.extension}`,\n };\n return status;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ExpoAudioStream.web.js","sourceRoot":"","sources":["../src/ExpoAudioStream.web.ts"],"names":[],"mappings":"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","sourcesContent":["// src/ExpoAudioStreamModule.web.ts\nimport { EventEmitter } from 'expo-modules-core'\n\nimport { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types'\nimport {\n AudioRecording,\n AudioStreamStatus,\n BitDepth,\n RecordingConfig,\n StartRecordingResult,\n} from './ExpoAudioStream.types'\nimport { WebRecorder } from './WebRecorder.web'\nimport { AudioEventPayload } from './events'\nimport { getLogger } from './logger'\nimport { concatenateBuffers } from './utils/concatenateBuffers'\nimport { encodingToBitDepth } from './utils/encodingToBitDepth'\nimport { writeWavHeader } from './utils/writeWavHeader'\n\nexport interface EmitAudioEventProps {\n data: Float32Array\n position: number\n}\nexport type EmitAudioEventFunction = (_: EmitAudioEventProps) => void\nexport type EmitAudioAnalysisFunction = (_: AudioAnalysis) => void\n\nexport interface ExpoAudioStreamWebProps {\n audioWorkletUrl: string\n featuresExtratorUrl: string\n}\n\nconst logger = getLogger('ExpoAudioStreamWeb')\n\nexport class ExpoAudioStreamWeb extends EventEmitter {\n customRecorder: WebRecorder | null\n audioChunks: ArrayBuffer[]\n isRecording: boolean\n isPaused: boolean\n recordingStartTime: number\n pausedTime: number\n currentDurationMs: number\n currentSize: number\n currentInterval: number\n lastEmittedSize: number\n lastEmittedTime: number\n streamUuid: string | null\n extension: 'webm' | 'wav' = 'wav' // Default extension is 'webm'\n recordingConfig?: RecordingConfig\n bitDepth: BitDepth // Bit depth of the audio\n audioWorkletUrl: string\n featuresExtratorUrl: string\n\n constructor({\n audioWorkletUrl,\n featuresExtratorUrl,\n }: ExpoAudioStreamWebProps) {\n const mockNativeModule = {\n addListener: () => {\n // Not used on web\n },\n removeListeners: () => {\n // Not used on web\n },\n }\n super(mockNativeModule) // Pass the mock native module to the parent class\n\n this.customRecorder = null\n this.audioChunks = []\n this.isRecording = false\n this.isPaused = false\n this.recordingStartTime = 0\n this.pausedTime = 0\n this.currentDurationMs = 0\n this.currentSize = 0\n this.bitDepth = 32 // Default\n this.currentInterval = 1000 // Default interval in ms\n this.lastEmittedSize = 0\n this.lastEmittedTime = 0\n this.streamUuid = null // Initialize UUID on first recording start\n this.audioWorkletUrl = audioWorkletUrl\n this.featuresExtratorUrl = featuresExtratorUrl\n }\n\n // Utility to handle user media stream\n async getMediaStream() {\n try {\n return await navigator.mediaDevices.getUserMedia({ audio: true })\n } catch (error) {\n console.error('Failed to get media stream:', error)\n throw error\n }\n }\n\n // Start recording with options\n async startRecording(recordingConfig: RecordingConfig = {}) {\n if (this.isRecording) {\n throw new Error('Recording is already in progress')\n }\n\n this.bitDepth = encodingToBitDepth({\n encoding: recordingConfig.encoding ?? 'pcm_32bit',\n })\n\n const audioContext = new (window.AudioContext ||\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore - Allow webkitAudioContext for Safari\n window.webkitAudioContext)()\n const stream = await this.getMediaStream()\n\n const source = audioContext.createMediaStreamSource(stream)\n\n this.customRecorder = new WebRecorder({\n audioContext,\n source,\n recordingConfig,\n audioWorkletUrl: this.audioWorkletUrl,\n emitAudioEventCallback: ({\n data,\n position,\n }: EmitAudioEventProps) => {\n this.audioChunks.push(data)\n this.currentSize += data.byteLength\n this.emitAudioEvent({ data, position })\n this.lastEmittedTime = Date.now()\n this.lastEmittedSize = this.currentSize\n },\n emitAudioAnalysisCallback: (audioAnalysisData: AudioAnalysis) => {\n logger.log(`Emitted AudioAnalysis:`, audioAnalysisData)\n this.emit('AudioAnalysis', audioAnalysisData)\n },\n })\n await this.customRecorder.init()\n this.customRecorder.start()\n\n // // Set a timer to stop recording after 5 seconds\n // setTimeout(() => {\n // logger.log(\"AUTO Stopping recording\");\n // this.customRecorder?.stopAndPlay();\n // this.isRecording = false;\n // }, 3000);\n\n this.isRecording = true\n this.recordingConfig = recordingConfig\n this.recordingStartTime = Date.now()\n this.pausedTime = 0\n this.lastEmittedSize = 0\n this.lastEmittedTime = 0\n this.streamUuid = Date.now().toString()\n const fileUri = `${this.streamUuid}.${this.extension}`\n const streamConfig: StartRecordingResult = {\n fileUri,\n mimeType: `audio/${this.extension}`,\n bitDepth: this.bitDepth,\n channels: recordingConfig.channels ?? 1,\n sampleRate: recordingConfig.sampleRate ?? 44100,\n }\n return streamConfig\n }\n\n emitAudioEvent({ data, position }: EmitAudioEventProps) {\n const fileUri = `${this.streamUuid}.${this.extension}`\n const audioEventPayload: AudioEventPayload = {\n fileUri,\n mimeType: `audio/${this.extension}`,\n lastEmittedSize: this.lastEmittedSize, // Since this might be continuously streaming, adjust accordingly\n deltaSize: data.byteLength,\n position,\n totalSize: this.currentSize,\n buffer: data,\n streamUuid: this.streamUuid ?? '', // Generate or manage UUID for stream identification\n }\n\n this.emit('AudioData', audioEventPayload)\n }\n\n // Stop recording\n async stopRecording(): Promise<AudioRecording> {\n if (!this.customRecorder) {\n throw new Error('Recorder is not initialized')\n }\n\n const fullPcmBufferArray = await this.customRecorder.stop()\n\n // concat all audio chunks\n logger.debug(`Stopped recording`, fullPcmBufferArray)\n this.isRecording = false\n this.currentDurationMs = Date.now() - this.recordingStartTime\n\n const wavConfig = {\n buffer: fullPcmBufferArray.buffer,\n sampleRate: this.recordingConfig?.sampleRate ?? 44100,\n numChannels: this.recordingConfig?.channels ?? 1,\n bitDepth: this.bitDepth,\n }\n logger.debug(`Writing wav header`, wavConfig)\n const wavBuffer = writeWavHeader(wavConfig).slice(0)\n\n // Create blob fileUri from audio chunks\n const blob = new Blob([wavBuffer], {\n type: `audio/${this.extension}`,\n })\n const fileUri = URL.createObjectURL(blob)\n\n const result: AudioRecording = {\n fileUri,\n filename: `${this.streamUuid}.${this.extension}`,\n wavPCMData: fullPcmBufferArray,\n bitDepth: this.bitDepth,\n channels: this.recordingConfig?.channels ?? 1,\n sampleRate: this.recordingConfig?.sampleRate ?? 44100,\n durationMs: this.currentDurationMs,\n size: this.currentSize,\n mimeType: `audio/${this.extension}`,\n }\n\n return result\n }\n\n // Pause recording\n async pauseRecording() {\n if (!this.isRecording || this.isPaused) {\n throw new Error('Recording is not active or already paused')\n }\n\n if (this.customRecorder) {\n this.customRecorder.pause()\n }\n this.isPaused = true\n this.pausedTime = Date.now()\n }\n\n // Resume recording\n async resumeRecording() {\n if (!this.isPaused) {\n throw new Error('Recording is not paused')\n }\n\n if (this.customRecorder) {\n this.customRecorder.resume()\n }\n this.isPaused = false\n this.recordingStartTime += Date.now() - this.pausedTime\n }\n\n // Get current status\n status() {\n const status: AudioStreamStatus = {\n isRecording: this.isRecording,\n isPaused: this.isPaused,\n durationMs: Date.now() - this.recordingStartTime,\n size: this.currentSize,\n interval: this.currentInterval,\n mimeType: `audio/${this.extension}`,\n }\n return status\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAudioStreamModule.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStreamModule.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ExpoAudioStreamModule.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStreamModule.ts"],"names":[],"mappings":"AASA,QAAA,IAAI,qBAAqB,EAAE,GAAG,CAAA;AAe9B,eAAe,qBAAqB,CAAA"}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import { requireNativeModule } from
|
|
2
|
-
import { Platform } from
|
|
3
|
-
import { ExpoAudioStreamWeb, } from
|
|
1
|
+
import { requireNativeModule } from 'expo-modules-core';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
import { ExpoAudioStreamWeb, } from './ExpoAudioStream.web';
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
5
|
let ExpoAudioStreamModule;
|
|
5
|
-
if (Platform.OS ===
|
|
6
|
+
if (Platform.OS === 'web') {
|
|
6
7
|
let instance = null;
|
|
7
|
-
ExpoAudioStreamModule = (webProps) => {
|
|
8
|
+
ExpoAudioStreamModule = ((webProps) => {
|
|
8
9
|
if (!instance) {
|
|
9
10
|
instance = new ExpoAudioStreamWeb(webProps);
|
|
10
11
|
}
|
|
11
12
|
return instance;
|
|
12
|
-
};
|
|
13
|
+
});
|
|
13
14
|
}
|
|
14
15
|
else {
|
|
15
|
-
ExpoAudioStreamModule = requireNativeModule(
|
|
16
|
+
ExpoAudioStreamModule = requireNativeModule('ExpoAudioStream');
|
|
16
17
|
}
|
|
17
18
|
export default ExpoAudioStreamModule;
|
|
18
19
|
//# sourceMappingURL=ExpoAudioStreamModule.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAudioStreamModule.js","sourceRoot":"","sources":["../src/ExpoAudioStreamModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"ExpoAudioStreamModule.js","sourceRoot":"","sources":["../src/ExpoAudioStreamModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEvC,OAAO,EACH,kBAAkB,GAErB,MAAM,uBAAuB,CAAA;AAE9B,8DAA8D;AAC9D,IAAI,qBAA0B,CAAA;AAE9B,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;IACxB,IAAI,QAAQ,GAA8B,IAAI,CAAA;IAE9C,qBAAqB,GAAG,CAAC,CAAC,QAAiC,EAAE,EAAE;QAC3D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACZ,QAAQ,GAAG,IAAI,kBAAkB,CAAC,QAAQ,CAAC,CAAA;QAC/C,CAAC;QACD,OAAO,QAAQ,CAAA;IACnB,CAAC,CAAC,CAAA;AACN,CAAC;KAAM,CAAC;IACJ,qBAAqB,GAAG,mBAAmB,CAAC,iBAAiB,CAAC,CAAA;AAClE,CAAC;AAED,eAAe,qBAAqB,CAAA","sourcesContent":["import { requireNativeModule } from 'expo-modules-core'\nimport { Platform } from 'react-native'\n\nimport {\n ExpoAudioStreamWeb,\n ExpoAudioStreamWebProps,\n} from './ExpoAudioStream.web'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet ExpoAudioStreamModule: any\n\nif (Platform.OS === 'web') {\n let instance: ExpoAudioStreamWeb | null = null\n\n ExpoAudioStreamModule = ((webProps: ExpoAudioStreamWebProps) => {\n if (!instance) {\n instance = new ExpoAudioStreamWeb(webProps)\n }\n return instance\n })\n} else {\n ExpoAudioStreamModule = requireNativeModule('ExpoAudioStream')\n}\n\nexport default ExpoAudioStreamModule\n"]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { RecordingConfig } from
|
|
3
|
-
import { EmitAudioAnalysisFunction, EmitAudioEventFunction } from
|
|
1
|
+
import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types';
|
|
2
|
+
import { RecordingConfig } from './ExpoAudioStream.types';
|
|
3
|
+
import { EmitAudioAnalysisFunction, EmitAudioEventFunction } from './ExpoAudioStream.web';
|
|
4
4
|
interface AudioFeaturesEvent {
|
|
5
5
|
data: {
|
|
6
6
|
command: string;
|
|
7
|
-
result:
|
|
7
|
+
result: AudioAnalysis;
|
|
8
8
|
};
|
|
9
9
|
}
|
|
10
10
|
export declare class WebRecorder {
|
|
@@ -20,13 +20,13 @@ export declare class WebRecorder {
|
|
|
20
20
|
private numberOfChannels;
|
|
21
21
|
private bitDepth;
|
|
22
22
|
private exportBitDepth;
|
|
23
|
-
private
|
|
23
|
+
private buffer;
|
|
24
|
+
private bufferSize;
|
|
24
25
|
private audioAnalysisData;
|
|
25
|
-
constructor({ audioContext, source, recordingConfig,
|
|
26
|
+
constructor({ audioContext, source, recordingConfig, audioWorkletUrl, emitAudioEventCallback, emitAudioAnalysisCallback, }: {
|
|
26
27
|
audioContext: AudioContext;
|
|
27
28
|
source: MediaStreamAudioSourceNode;
|
|
28
29
|
recordingConfig: RecordingConfig;
|
|
29
|
-
featuresExtratorUrl: string;
|
|
30
30
|
audioWorkletUrl: string;
|
|
31
31
|
emitAudioEventCallback: EmitAudioEventFunction;
|
|
32
32
|
emitAudioAnalysisCallback: EmitAudioAnalysisFunction;
|
|
@@ -37,7 +37,7 @@ export declare class WebRecorder {
|
|
|
37
37
|
handleWorkerError(error: ErrorEvent): void;
|
|
38
38
|
handleFeatureExtractorMessage(event: AudioFeaturesEvent): void;
|
|
39
39
|
start(): void;
|
|
40
|
-
stop(): Promise<
|
|
40
|
+
stop(): Promise<Float32Array>;
|
|
41
41
|
pause(): void;
|
|
42
42
|
stopMediaStreamTracks(): void;
|
|
43
43
|
playRecordedData({ recordedData, }: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebRecorder.web.d.ts","sourceRoot":"","sources":["../src/WebRecorder.web.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"WebRecorder.web.d.ts","sourceRoot":"","sources":["../src/WebRecorder.web.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EACH,yBAAyB,EACzB,sBAAsB,EACzB,MAAM,uBAAuB,CAAA;AAc9B,UAAU,kBAAkB;IACxB,IAAI,EAAE;QACF,OAAO,EAAE,MAAM,CAAA;QACf,MAAM,EAAE,aAAa,CAAA;KACxB,CAAA;CACJ;AAWD,qBAAa,WAAW;IACpB,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,sBAAsB,CAAC,CAAQ;IACvC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,sBAAsB,CAAwB;IACtD,OAAO,CAAC,yBAAyB,CAA2B;IAC5D,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,gBAAgB,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,iBAAiB,CAAe;gBAE5B,EACR,YAAY,EACZ,MAAM,EACN,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,yBAAyB,GAC5B,EAAE;QACC,YAAY,EAAE,YAAY,CAAA;QAC1B,MAAM,EAAE,0BAA0B,CAAA;QAClC,eAAe,EAAE,eAAe,CAAA;QAChC,eAAe,EAAE,MAAM,CAAA;QACvB,sBAAsB,EAAE,sBAAsB,CAAA;QAC9C,yBAAyB,EAAE,yBAAyB,CAAA;KACvD;IAkDK,IAAI;IA6FV,0BAA0B,CAAC,mBAAmB,CAAC,EAAE,MAAM;IA0BvD,kBAAkB;IAqBlB,iBAAiB,CAAC,KAAK,EAAE,UAAU;IAInC,6BAA6B,CAAC,KAAK,EAAE,kBAAkB;IAgCvD,KAAK;IAKL,IAAI,IAAI,OAAO,CAAC,YAAY,CAAC;IAsE7B,KAAK;IAML,qBAAqB;IAMf,gBAAgB,CAAC,EACnB,YAAY,GACf,EAAE;QACC,YAAY,EAAE,WAAW,CAAA;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;KACpB;IAsBD,OAAO,CAAC,uBAAuB;IAoB/B,MAAM;CAKT"}
|