@siteed/expo-audio-stream 1.8.0 → 1.9.0
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/CHANGELOG.md +18 -1
- package/build/ExpoAudioStream.types.d.ts +1 -14
- package/build/ExpoAudioStream.types.d.ts.map +1 -1
- package/build/ExpoAudioStream.types.js.map +1 -1
- package/build/ExpoAudioStream.web.d.ts +5 -3
- package/build/ExpoAudioStream.web.d.ts.map +1 -1
- package/build/ExpoAudioStream.web.js +52 -57
- package/build/ExpoAudioStream.web.js.map +1 -1
- package/build/WebRecorder.web.d.ts +7 -5
- package/build/WebRecorder.web.d.ts.map +1 -1
- package/build/WebRecorder.web.js +150 -197
- package/build/WebRecorder.web.js.map +1 -1
- package/build/useAudioRecorder.d.ts +2 -2
- package/build/useAudioRecorder.d.ts.map +1 -1
- package/build/useAudioRecorder.js.map +1 -1
- package/build/workers/inlineAudioWebWorker.web.d.ts +1 -1
- package/build/workers/inlineAudioWebWorker.web.d.ts.map +1 -1
- package/build/workers/inlineAudioWebWorker.web.js +65 -160
- package/build/workers/inlineAudioWebWorker.web.js.map +1 -1
- package/package.json +1 -1
- package/src/ExpoAudioStream.types.ts +1 -15
- package/src/ExpoAudioStream.web.ts +55 -58
- package/src/WebRecorder.web.ts +182 -228
- package/src/useAudioRecorder.tsx +1 -2
- package/src/workers/inlineAudioWebWorker.web.tsx +65 -160
package/CHANGELOG.md
CHANGED
|
@@ -8,18 +8,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
## [1.9.0] - 2025-01-11
|
|
12
|
+
- feat(web-audio): optimize memory usage and streaming performance for web audio recording (#75) ([7b93e12](https://github.com/deeeed/expo-audio-stream/commit/7b93e12aae4bc0599b06b48ca34a60f65587fc75))
|
|
13
|
+
|
|
11
14
|
## [1.8.0] - 2025-01-10
|
|
12
15
|
- feat(audio): implement audio compression support ([ff4e060](https://github.com/deeeed/expo-audio-stream/commit/ff4e060fef1061804c1cc0126d4344d2d50daa9a))
|
|
13
16
|
|
|
17
|
+
|
|
14
18
|
## [1.7.2] - 2025-01-07
|
|
15
19
|
- fix(audio-stream): correct WAV header handling in web audio recording ([9ba7de5](https://github.com/deeeed/expo-audio-stream/commit/9ba7de5b96ca4cc937dea261c80d3fda9c99e8f4))
|
|
16
20
|
|
|
17
21
|
|
|
22
|
+
|
|
18
23
|
## [1.7.1] - 2025-01-07
|
|
19
24
|
- update notification to avoid triggering new alerts (#71) ([32dcfc5](https://github.com/deeeed/expo-audio-stream/commit/32dcfc55daf3236babefc17016f329c177d466fd))
|
|
20
25
|
|
|
21
26
|
|
|
22
27
|
|
|
28
|
+
|
|
23
29
|
## [1.7.0] - 2025-01-05
|
|
24
30
|
- feat(playground): enhance app configuration and build setup for production deployment (#58) ([929d443](https://github.com/deeeed/expo-audio-stream/commit/929d443145378b1430d215db5c00b13758420e2b))
|
|
25
31
|
- chore(expo-audio-stream): release @siteed/expo-audio-stream@1.6.1 ([084e8ad](https://github.com/deeeed/expo-audio-stream/commit/084e8adb91da7874c9e608b55d9c7b2ffd7a8327))
|
|
@@ -31,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
31
37
|
|
|
32
38
|
|
|
33
39
|
|
|
40
|
+
|
|
34
41
|
## [1.6.1] - 2024-12-11
|
|
35
42
|
- chore(expo-audio-stream): remove git commit step from publish script ([4a772ce](https://github.com/deeeed/expo-audio-stream/commit/4a772ce93bb7405d9b8e981f46bdf8941a71ecfe))
|
|
36
43
|
- chore: more publishing automation ([3693021](https://github.com/deeeed/expo-audio-stream/commit/369302107f9dca9dddd8ae68e6214481a39976ac))
|
|
@@ -46,6 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
46
53
|
|
|
47
54
|
|
|
48
55
|
|
|
56
|
+
|
|
49
57
|
## [1.5.0] - 2024-12-10
|
|
50
58
|
- UNPUBLISHED because of a bug in the build system
|
|
51
59
|
|
|
@@ -54,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
54
62
|
|
|
55
63
|
|
|
56
64
|
|
|
65
|
+
|
|
57
66
|
## [1.4.0] - 2024-12-05
|
|
58
67
|
- chore: remove unusded dependencies ([ad81dd5](https://github.com/deeeed/expo-audio-stream/commit/ad81dd560c93dd1d04995a323a4ae72d4de20f3e))
|
|
59
68
|
|
|
@@ -62,6 +71,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
62
71
|
|
|
63
72
|
|
|
64
73
|
|
|
74
|
+
|
|
65
75
|
## [1.3.1] - 2024-12-05
|
|
66
76
|
- feat(web): implement throttling and optimize event processing (#49) ([da28765](https://github.com/deeeed/expo-audio-stream/commit/da2876524c2c9d6e0a980fde40a0197b929d8a7f))
|
|
67
77
|
|
|
@@ -70,6 +80,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
70
80
|
|
|
71
81
|
|
|
72
82
|
|
|
83
|
+
|
|
73
84
|
## [1.3.0] - 2024-11-28
|
|
74
85
|
### Added
|
|
75
86
|
- refactor(permissions): standardize permission status response structure across platforms (#44) ([7c9c800](https://github.com/deeeed/expo-audio-stream/commit/7c9c800d83b7cea3516643371484d5e1f3b99e4c))
|
|
@@ -83,6 +94,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
83
94
|
|
|
84
95
|
|
|
85
96
|
|
|
97
|
+
|
|
86
98
|
## [1.2.5] - 2024-11-12
|
|
87
99
|
### Added
|
|
88
100
|
- docs(license): add MIT license to all packages (6 files changed)
|
|
@@ -93,6 +105,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
93
105
|
|
|
94
106
|
|
|
95
107
|
|
|
108
|
+
|
|
96
109
|
## [1.2.4] - 2024-11-05
|
|
97
110
|
### Changed
|
|
98
111
|
- Android minimum audio interval set to 10ms.
|
|
@@ -106,6 +119,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
106
119
|
|
|
107
120
|
|
|
108
121
|
|
|
122
|
+
|
|
109
123
|
## [1.2.0] - 2024-10-24
|
|
110
124
|
### Added
|
|
111
125
|
- Feature: Keep device awake during recording with `keepAwake` option
|
|
@@ -119,6 +133,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
119
133
|
|
|
120
134
|
|
|
121
135
|
|
|
136
|
+
|
|
122
137
|
## [1.1.17] - 2024-10-21
|
|
123
138
|
### Added
|
|
124
139
|
- Support bluetooth headset on ios
|
|
@@ -129,6 +144,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
129
144
|
|
|
130
145
|
|
|
131
146
|
|
|
147
|
+
|
|
132
148
|
## [1.0.0] - 2024-04-01
|
|
133
149
|
### Added
|
|
134
150
|
- Initial release of @siteed/expo-audio-stream.
|
|
@@ -139,7 +155,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
139
155
|
- Feature: Audio features extraction during recording.
|
|
140
156
|
- Feature: Consistent WAV PCM recording format across all platforms.
|
|
141
157
|
|
|
142
|
-
[unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.
|
|
158
|
+
[unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.9.0...HEAD
|
|
159
|
+
[1.9.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.8.0...@siteed/expo-audio-stream@1.9.0
|
|
143
160
|
[1.8.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.7.2...@siteed/expo-audio-stream@1.8.0
|
|
144
161
|
[1.7.2]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.7.1...@siteed/expo-audio-stream@1.7.2
|
|
145
162
|
[1.7.1]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.7.0...@siteed/expo-audio-stream@1.7.1
|
|
@@ -56,7 +56,6 @@ export interface AudioRecording {
|
|
|
56
56
|
bitDepth: BitDepth;
|
|
57
57
|
sampleRate: SampleRate;
|
|
58
58
|
transcripts?: TranscriberData[];
|
|
59
|
-
wavPCMData?: Float32Array;
|
|
60
59
|
analysisData?: AudioAnalysis;
|
|
61
60
|
compression?: CompressionInfo & {
|
|
62
61
|
compressedFileUri: string;
|
|
@@ -134,21 +133,9 @@ export interface WaveformConfig {
|
|
|
134
133
|
mirror?: boolean;
|
|
135
134
|
height?: number;
|
|
136
135
|
}
|
|
137
|
-
export interface WebRecordingOptions {
|
|
138
|
-
/**
|
|
139
|
-
* Web-specific option to skip the final audio data consolidation process.
|
|
140
|
-
* When true, it will:
|
|
141
|
-
* - Skip the time-consuming process of concatenating all audio chunks
|
|
142
|
-
* - Return immediately with the compressed audio (if compression is enabled)
|
|
143
|
-
* - Improve performance when stopping large recordings
|
|
144
|
-
* - Useful when only the compressed audio is needed (e.g., when not using transcription)
|
|
145
|
-
* @default false
|
|
146
|
-
*/
|
|
147
|
-
skipFinalConsolidation?: boolean;
|
|
148
|
-
}
|
|
149
136
|
export interface UseAudioRecorderState {
|
|
150
137
|
startRecording: (_: RecordingConfig) => Promise<StartRecordingResult>;
|
|
151
|
-
stopRecording: (
|
|
138
|
+
stopRecording: () => Promise<AudioRecording | null>;
|
|
152
139
|
pauseRecording: () => Promise<void>;
|
|
153
140
|
resumeRecording: () => Promise<void>;
|
|
154
141
|
isRecording: boolean;
|
|
@@ -1 +1 @@
|
|
|
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,eAAe;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACjB;AAED,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;IAChB,WAAW,CAAC,EAAE,eAAe,CAAA;CAChC;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;IACjB,WAAW,CAAC,EAAE,eAAe,GAAG;QAC5B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KACvB,CAAA;CACJ;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,MAAM,WAAW,GAAG;IACtB,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,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,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,OAAO,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,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,
|
|
1
|
+
{"version":3,"file":"ExpoAudioStream.types.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStream.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -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 AmplitudeAlgorithm,\n AudioAnalysis,\n AudioFeaturesOptions,\n} from './AudioAnalysis/AudioAnalysis.types'\nimport { AudioAnalysisEvent } from './events'\n\nexport interface CompressionInfo {\n size: number\n mimeType: string\n bitrate: number\n format: string\n}\n\nexport interface AudioStreamStatus {\n isRecording: boolean\n isPaused: boolean\n durationMs: number\n size: number\n interval: number\n mimeType: string\n compression?: CompressionInfo\n}\n\nexport interface AudioDataEvent {\n data: string | Float32Array\n position: number\n fileUri: string\n eventDataSize: number\n totalSize: number\n compression?: CompressionInfo & {\n data?: string | Blob // Base64 (native) or Float32Array (web) encoded compressed data chunk\n }\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 type 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 interface Chunk {\n text: string\n timestamp: [number, number | null]\n}\n\nexport interface TranscriberData {\n id: string\n isBusy: boolean\n text: string\n startTime: number\n endTime: number\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
|
|
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 CompressionInfo {\n size: number\n mimeType: string\n bitrate: number\n format: string\n}\n\nexport interface AudioStreamStatus {\n isRecording: boolean\n isPaused: boolean\n durationMs: number\n size: number\n interval: number\n mimeType: string\n compression?: CompressionInfo\n}\n\nexport interface AudioDataEvent {\n data: string | Float32Array\n position: number\n fileUri: string\n eventDataSize: number\n totalSize: number\n compression?: CompressionInfo & {\n data?: string | Blob // Base64 (native) or Float32Array (web) encoded compressed data chunk\n }\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 type 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 interface Chunk {\n text: string\n timestamp: [number, number | null]\n}\n\nexport interface TranscriberData {\n id: string\n isBusy: boolean\n text: string\n startTime: number\n endTime: number\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 analysisData?: AudioAnalysis // Analysis data for the recording depending on enableProcessing flag\n compression?: CompressionInfo & {\n compressedFileUri: string\n }\n}\n\nexport interface StartRecordingResult {\n fileUri: string\n mimeType: string\n channels?: number\n bitDepth?: BitDepth\n sampleRate?: SampleRate\n compression?: CompressionInfo & {\n compressedFileUri: string\n }\n}\n\nexport interface AudioSessionConfig {\n category?:\n | 'Ambient'\n | 'SoloAmbient'\n | 'Playback'\n | 'Record'\n | 'PlayAndRecord'\n | 'MultiRoute'\n mode?:\n | 'Default'\n | 'VoiceChat'\n | 'VideoChat'\n | 'GameChat'\n | 'VideoRecording'\n | 'Measurement'\n | 'MoviePlayback'\n | 'SpokenAudio'\n categoryOptions?: (\n | 'MixWithOthers'\n | 'DuckOthers'\n | 'InterruptSpokenAudioAndMixWithOthers'\n | 'AllowBluetooth'\n | 'AllowBluetoothA2DP'\n | 'AllowAirPlay'\n | 'DefaultToSpeaker'\n )[]\n}\n\nexport interface IOSConfig {\n audioSession?: AudioSessionConfig\n}\n\nexport interface RecordingConfig {\n // Sample rate for recording (16000, 44100, or 48000 Hz)\n sampleRate?: SampleRate\n\n // Number of audio channels (1 for mono, 2 for stereo)\n channels?: 1 | 2\n\n // Encoding type for the recording (pcm_32bit, pcm_16bit, pcm_8bit)\n encoding?: EncodingType\n\n // Interval in milliseconds at which to emit recording data\n interval?: number\n\n // Keep the device awake while recording (default is false)\n keepAwake?: boolean\n\n // Show a notification during recording (default is false)\n showNotification?: boolean\n\n // Show waveform in the notification (Android only, when showNotification is true)\n showWaveformInNotification?: boolean\n\n // Configuration for the notification\n notification?: NotificationConfig\n\n // Enable audio processing (default is false)\n enableProcessing?: boolean\n\n // iOS-specific configuration\n ios?: IOSConfig\n\n // Number of data points to extract per second of audio (default is 1000)\n pointsPerSecond?: number\n\n // Algorithm to use for amplitude computation (default is \"rms\")\n algorithm?: AmplitudeAlgorithm\n\n // Feature options to extract (default is empty)\n features?: AudioFeaturesOptions\n\n // Callback function to handle audio stream\n onAudioStream?: (_: AudioDataEvent) => Promise<void>\n\n // Callback function to handle audio features extraction results\n onAudioAnalysis?: (_: AudioAnalysisEvent) => Promise<void>\n\n compression?: {\n enabled: boolean\n format: 'aac' | 'opus' | 'mp3'\n bitrate?: number\n }\n}\n\nexport interface NotificationConfig {\n // Title of the notification\n title?: string\n\n // Main text content of the notification\n text?: string\n\n // Icon to be displayed in the notification (resource name or URI)\n icon?: string\n\n // Android-specific notification configuration\n android?: {\n // Unique identifier for the notification channel\n channelId?: string\n\n // User-visible name of the notification channel\n channelName?: string\n\n // User-visible description of the notification channel\n channelDescription?: string\n\n // Unique identifier for this notification\n notificationId?: number\n\n // List of actions that can be performed from the notification\n actions?: NotificationAction[]\n\n // Configuration for the waveform visualization in the notification\n waveform?: WaveformConfig\n\n // Color of the notification LED (if device supports it)\n lightColor?: string\n\n // Priority of the notification (affects how it's displayed)\n priority?: 'min' | 'low' | 'default' | 'high' | 'max'\n\n // Accent color for the notification (used for the app icon and buttons)\n accentColor?: string\n }\n\n // iOS-specific notification configuration\n ios?: {\n // Identifier for the notification category (used for grouping similar notifications)\n categoryIdentifier?: string\n }\n}\n\nexport interface NotificationAction {\n // Display title for the action\n title: string\n\n // Unique identifier for the action\n identifier: string\n\n // Icon to be displayed for the action (Android only)\n icon?: string\n}\n\nexport interface WaveformConfig {\n color?: string // The color of the waveform (e.g., \"#FFFFFF\" for white)\n opacity?: number // Opacity of the waveform (0.0 - 1.0)\n strokeWidth?: number // Width of the waveform line (default: 1.5)\n style?: 'stroke' | 'fill' // Drawing style: \"stroke\" for outline, \"fill\" for solid\n mirror?: boolean // Whether to mirror the waveform (symmetrical display)\n height?: number // Height of the waveform view in dp (default: 64)\n}\n\nexport interface UseAudioRecorderState {\n startRecording: (_: RecordingConfig) => Promise<StartRecordingResult>\n stopRecording: () => Promise<AudioRecording | null>\n pauseRecording: () => Promise<void>\n resumeRecording: () => Promise<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 compression?: CompressionInfo\n analysisData?: AudioAnalysis // Analysis data for the recording depending on enableProcessing flag\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LegacyEventEmitter } from 'expo-modules-core';
|
|
2
2
|
import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types';
|
|
3
|
-
import { AudioRecording, AudioStreamStatus, BitDepth, ConsoleLike, RecordingConfig, StartRecordingResult
|
|
3
|
+
import { AudioRecording, AudioStreamStatus, BitDepth, ConsoleLike, RecordingConfig, StartRecordingResult } from './ExpoAudioStream.types';
|
|
4
4
|
import { WebRecorder } from './WebRecorder.web';
|
|
5
5
|
export interface EmitAudioEventProps {
|
|
6
6
|
data: Float32Array;
|
|
@@ -20,6 +20,7 @@ export interface ExpoAudioStreamWebProps {
|
|
|
20
20
|
logger?: ConsoleLike;
|
|
21
21
|
audioWorkletUrl: string;
|
|
22
22
|
featuresExtratorUrl: string;
|
|
23
|
+
maxBufferSize?: number;
|
|
23
24
|
}
|
|
24
25
|
export declare class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
25
26
|
customRecorder: WebRecorder | null;
|
|
@@ -43,11 +44,12 @@ export declare class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
43
44
|
logger?: ConsoleLike;
|
|
44
45
|
latestPosition: number;
|
|
45
46
|
totalCompressedSize: number;
|
|
46
|
-
|
|
47
|
+
private readonly maxBufferSize;
|
|
48
|
+
constructor({ audioWorkletUrl, featuresExtratorUrl, logger, maxBufferSize, }: ExpoAudioStreamWebProps);
|
|
47
49
|
getMediaStream(): Promise<MediaStream>;
|
|
48
50
|
startRecording(recordingConfig?: RecordingConfig): Promise<StartRecordingResult>;
|
|
49
51
|
emitAudioEvent({ data, position, compression }: EmitAudioEventProps): void;
|
|
50
|
-
stopRecording(
|
|
52
|
+
stopRecording(): Promise<AudioRecording>;
|
|
51
53
|
pauseRecording(): Promise<void>;
|
|
52
54
|
resumeRecording(): Promise<void>;
|
|
53
55
|
status(): AudioStreamStatus;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoAudioStream.web.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStream.web.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AACnE,OAAO,EACH,cAAc,EACd,iBAAiB,EACjB,QAAQ,EACR,WAAW,EACX,eAAe,EACf,oBAAoB,
|
|
1
|
+
{"version":3,"file":"ExpoAudioStream.web.d.ts","sourceRoot":"","sources":["../src/ExpoAudioStream.web.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AACnE,OAAO,EACH,cAAc,EACd,iBAAiB,EACjB,QAAQ,EACR,WAAW,EACX,eAAe,EACf,oBAAoB,EACvB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAI/C,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,YAAY,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE;QACV,IAAI,EAAE,IAAI,CAAA;QACV,IAAI,EAAE,MAAM,CAAA;QACZ,SAAS,EAAE,MAAM,CAAA;QACjB,QAAQ,EAAE,MAAM,CAAA;QAChB,MAAM,EAAE,MAAM,CAAA;QACd,OAAO,EAAE,MAAM,CAAA;KAClB,CAAA;CACJ;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,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;IACvB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,qBAAa,kBAAmB,SAAQ,kBAAkB;IACtD,cAAc,EAAE,WAAW,GAAG,IAAI,CAAA;IAClC,WAAW,EAAE,YAAY,EAAE,CAAA;IAC3B,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,0BAA0B,EAAE,MAAM,CAAA;IAClC,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;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,cAAc,EAAE,MAAM,CAAI;IAC1B,mBAAmB,EAAE,MAAM,CAAI;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;gBAE1B,EACR,eAAe,EACf,mBAAmB,EACnB,MAAM,EACN,aAAmB,GACtB,EAAE,uBAAuB;IAiCpB,cAAc;IAUd,cAAc,CAAC,eAAe,GAAE,eAAoB;IAoF1D,cAAc,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,mBAAmB;IAgC7D,aAAa,IAAI,OAAO,CAAC,cAAc,CAAC;IA4DxC,cAAc;IAad,eAAe;IAarB,MAAM;CAoBT"}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { LegacyEventEmitter } from 'expo-modules-core';
|
|
3
3
|
import { WebRecorder } from './WebRecorder.web';
|
|
4
4
|
import { encodingToBitDepth } from './utils/encodingToBitDepth';
|
|
5
|
-
import { writeWavHeader } from './utils/writeWavHeader';
|
|
6
5
|
export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
7
6
|
customRecorder;
|
|
8
7
|
audioChunks;
|
|
@@ -25,7 +24,9 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
25
24
|
logger;
|
|
26
25
|
latestPosition = 0;
|
|
27
26
|
totalCompressedSize = 0;
|
|
28
|
-
|
|
27
|
+
maxBufferSize;
|
|
28
|
+
constructor({ audioWorkletUrl, featuresExtratorUrl, logger, maxBufferSize = 100, // Default to storing last 100 chunks (1 chunk = 0.5 seconds)
|
|
29
|
+
}) {
|
|
29
30
|
const mockNativeModule = {
|
|
30
31
|
addListener: () => {
|
|
31
32
|
// Not used on web
|
|
@@ -53,6 +54,7 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
53
54
|
this.streamUuid = null; // Initialize UUID on first recording start
|
|
54
55
|
this.audioWorkletUrl = audioWorkletUrl;
|
|
55
56
|
this.featuresExtratorUrl = featuresExtratorUrl;
|
|
57
|
+
this.maxBufferSize = maxBufferSize;
|
|
56
58
|
}
|
|
57
59
|
// Utility to handle user media stream
|
|
58
60
|
async getMediaStream() {
|
|
@@ -85,7 +87,11 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
85
87
|
recordingConfig,
|
|
86
88
|
audioWorkletUrl: this.audioWorkletUrl,
|
|
87
89
|
emitAudioEventCallback: ({ data, position, compression, }) => {
|
|
90
|
+
// Keep only the latest chunks based on maxBufferSize
|
|
88
91
|
this.audioChunks.push(new Float32Array(data));
|
|
92
|
+
if (this.audioChunks.length > this.maxBufferSize) {
|
|
93
|
+
this.audioChunks.shift(); // Remove oldest chunk
|
|
94
|
+
}
|
|
89
95
|
this.currentSize += data.byteLength;
|
|
90
96
|
this.emitAudioEvent({ data, position, compression });
|
|
91
97
|
this.lastEmittedTime = Date.now();
|
|
@@ -163,65 +169,54 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
163
169
|
this.emit('AudioData', audioEventPayload);
|
|
164
170
|
}
|
|
165
171
|
// Stop recording
|
|
166
|
-
async stopRecording(
|
|
172
|
+
async stopRecording() {
|
|
167
173
|
if (!this.customRecorder) {
|
|
168
174
|
throw new Error('Recorder is not initialized');
|
|
169
175
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const compressedUri = URL.createObjectURL(compressedBlob);
|
|
198
|
-
compression = {
|
|
199
|
-
compressedFileUri: compressedUri,
|
|
200
|
-
size: compressedBlob.size,
|
|
201
|
-
mimeType: 'audio/webm',
|
|
202
|
-
format: 'opus',
|
|
203
|
-
bitrate: this.recordingConfig.compression.bitrate ?? 128000,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
resolve({
|
|
207
|
-
fileUri,
|
|
208
|
-
filename: `${this.streamUuid}.${this.extension}`,
|
|
209
|
-
wavPCMData: new Float32Array(cloneableBuffer),
|
|
210
|
-
bitDepth: this.bitDepth,
|
|
211
|
-
channels: this.recordingConfig?.channels ?? 1,
|
|
212
|
-
sampleRate: this.recordingConfig?.sampleRate ?? 44100,
|
|
213
|
-
durationMs: this.currentDurationMs,
|
|
214
|
-
size: this.currentSize,
|
|
215
|
-
mimeType: `audio/${this.extension}`,
|
|
216
|
-
compression,
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
})().catch((error) => {
|
|
220
|
-
this.logger?.error('Error in stopRecording:', error);
|
|
221
|
-
throw error;
|
|
222
|
-
});
|
|
176
|
+
this.logger?.debug('[Stop] Starting stop process');
|
|
177
|
+
const startTime = performance.now();
|
|
178
|
+
try {
|
|
179
|
+
this.logger?.debug('[Stop] Stopping recorder');
|
|
180
|
+
const { compressedBlob } = await this.customRecorder.stop();
|
|
181
|
+
this.isRecording = false;
|
|
182
|
+
this.isPaused = false;
|
|
183
|
+
this.currentDurationMs = Date.now() - this.recordingStartTime;
|
|
184
|
+
let compression;
|
|
185
|
+
let fileUri = `${this.streamUuid}.${this.extension}`;
|
|
186
|
+
let mimeType = `audio/${this.extension}`;
|
|
187
|
+
if (compressedBlob && this.recordingConfig?.compression?.enabled) {
|
|
188
|
+
const compressedUri = URL.createObjectURL(compressedBlob);
|
|
189
|
+
compression = {
|
|
190
|
+
compressedFileUri: compressedUri,
|
|
191
|
+
size: compressedBlob.size,
|
|
192
|
+
mimeType: 'audio/webm',
|
|
193
|
+
format: 'opus',
|
|
194
|
+
bitrate: this.recordingConfig.compression.bitrate ?? 128000,
|
|
195
|
+
};
|
|
196
|
+
// Use compressed values when compression is enabled
|
|
197
|
+
fileUri = compressedUri;
|
|
198
|
+
mimeType = 'audio/webm';
|
|
199
|
+
}
|
|
200
|
+
this.logger?.debug(`[Stop] Completed stop process in ${performance.now() - startTime}ms`, {
|
|
201
|
+
durationMs: this.currentDurationMs,
|
|
202
|
+
compressedSize: compression?.size,
|
|
223
203
|
});
|
|
224
|
-
|
|
204
|
+
return {
|
|
205
|
+
fileUri,
|
|
206
|
+
filename: `${this.streamUuid}.${this.extension}`,
|
|
207
|
+
bitDepth: this.bitDepth,
|
|
208
|
+
channels: this.recordingConfig?.channels ?? 1,
|
|
209
|
+
sampleRate: this.recordingConfig?.sampleRate ?? 44100,
|
|
210
|
+
durationMs: this.currentDurationMs,
|
|
211
|
+
size: this.currentSize,
|
|
212
|
+
mimeType,
|
|
213
|
+
compression,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
this.logger?.error('[Stop] Error stopping recording:', error);
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
225
220
|
}
|
|
226
221
|
// Pause recording
|
|
227
222
|
async pauseRecording() {
|
|
@@ -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 { LegacyEventEmitter } from 'expo-modules-core'\n\nimport { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types'\nimport {\n AudioRecording,\n AudioStreamStatus,\n BitDepth,\n ConsoleLike,\n RecordingConfig,\n StartRecordingResult,\n WebRecordingOptions,\n} from './ExpoAudioStream.types'\nimport { WebRecorder } from './WebRecorder.web'\nimport { AudioEventPayload } from './events'\nimport { encodingToBitDepth } from './utils/encodingToBitDepth'\nimport { writeWavHeader } from './utils/writeWavHeader'\n\nexport interface EmitAudioEventProps {\n data: Float32Array\n position: number\n compression?: {\n data: Blob\n size: number\n totalSize: number\n mimeType: string\n format: string\n bitrate: number\n }\n}\nexport type EmitAudioEventFunction = (_: EmitAudioEventProps) => void\nexport type EmitAudioAnalysisFunction = (_: AudioAnalysis) => void\n\nexport interface ExpoAudioStreamWebProps {\n logger?: ConsoleLike\n audioWorkletUrl: string\n featuresExtratorUrl: string\n}\n\nexport class ExpoAudioStreamWeb extends LegacyEventEmitter {\n customRecorder: WebRecorder | null\n audioChunks: Float32Array[]\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 lastEmittedCompressionSize: 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 logger?: ConsoleLike\n latestPosition: number = 0\n totalCompressedSize: number = 0\n\n constructor({\n audioWorkletUrl,\n featuresExtratorUrl,\n logger,\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.logger = logger\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.latestPosition = 0\n this.lastEmittedCompressionSize = 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 this.logger?.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 logger: this.logger,\n audioContext,\n source,\n recordingConfig,\n audioWorkletUrl: this.audioWorkletUrl,\n emitAudioEventCallback: ({\n data,\n position,\n compression,\n }: EmitAudioEventProps) => {\n this.audioChunks.push(new Float32Array(data))\n this.currentSize += data.byteLength\n this.emitAudioEvent({ data, position, compression })\n this.lastEmittedTime = Date.now()\n this.lastEmittedSize = this.currentSize\n this.lastEmittedCompressionSize = compression?.size ?? 0\n },\n emitAudioAnalysisCallback: (audioAnalysisData: AudioAnalysis) => {\n this.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.isPaused = false\n this.lastEmittedSize = 0\n this.lastEmittedTime = 0\n this.lastEmittedCompressionSize = 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 compression: recordingConfig.compression\n ? {\n ...recordingConfig.compression,\n bitrate: recordingConfig.compression?.bitrate ?? 128000,\n size: 0,\n mimeType: 'audio/webm',\n format: recordingConfig.compression?.format ?? 'opus',\n compressedFileUri: '',\n }\n : undefined,\n }\n return streamConfig\n }\n\n emitAudioEvent({ data, position, compression }: EmitAudioEventProps) {\n const fileUri = `${this.streamUuid}.${this.extension}`\n if (compression?.size) {\n this.lastEmittedCompressionSize = compression.size\n this.totalCompressedSize = compression.totalSize\n }\n this.latestPosition = position\n this.currentDurationMs = position * 1000 // Convert position (in seconds) to ms\n\n const audioEventPayload: AudioEventPayload = {\n fileUri,\n mimeType: `audio/${this.extension}`,\n lastEmittedSize: this.lastEmittedSize,\n deltaSize: data.byteLength,\n position,\n totalSize: this.currentSize,\n buffer: data,\n streamUuid: this.streamUuid ?? '',\n compression: compression\n ? {\n data: compression?.data,\n totalSize: this.totalCompressedSize,\n eventDataSize: compression?.size ?? 0,\n position,\n }\n : undefined,\n }\n\n this.emit('AudioData', audioEventPayload)\n }\n\n // Stop recording\n async stopRecording(options?: WebRecordingOptions): Promise<AudioRecording> {\n if (!this.customRecorder) {\n throw new Error('Recorder is not initialized')\n }\n\n // Create a promise to handle the PCM data processing\n return new Promise<AudioRecording>((resolve) => {\n // Use requestAnimationFrame to avoid blocking the UI\n requestAnimationFrame(() => {\n // Move the async work inside a self-executing async function\n (async () => {\n const { pcmData, compressedBlob } = await this.customRecorder!.stop(options)\n\n this.logger?.debug(`Stopped recording`, pcmData)\n this.isRecording = false\n this.isPaused = false\n this.currentDurationMs = Date.now() - this.recordingStartTime\n\n // Process in the next frame to avoid blocking\n requestAnimationFrame(() => {\n // Rest of the code remains the same\n const wavBuffer = writeWavHeader({\n buffer: pcmData.buffer,\n sampleRate: this.recordingConfig?.sampleRate ?? 44100,\n numChannels: this.recordingConfig?.channels ?? 1,\n bitDepth: this.bitDepth,\n })\n\n const cloneableBuffer = wavBuffer.slice(0)\n\n const blob = new Blob([cloneableBuffer], {\n type: `audio/${this.extension}`,\n })\n const fileUri = URL.createObjectURL(blob)\n\n let compression: AudioRecording['compression']\n if (compressedBlob && this.recordingConfig?.compression?.enabled) {\n const compressedUri = URL.createObjectURL(compressedBlob)\n compression = {\n compressedFileUri: compressedUri,\n size: compressedBlob.size,\n mimeType: 'audio/webm',\n format: 'opus',\n bitrate: this.recordingConfig.compression.bitrate ?? 128000,\n }\n }\n\n resolve({\n fileUri,\n filename: `${this.streamUuid}.${this.extension}`,\n wavPCMData: new Float32Array(cloneableBuffer),\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 compression,\n })\n })\n })().catch((error) => {\n this.logger?.error('Error in stopRecording:', error)\n throw error\n })\n })\n })\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: this.currentDurationMs,\n size: this.currentSize,\n interval: this.currentInterval,\n mimeType: `audio/${this.extension}`,\n compression: this.recordingConfig?.compression?.enabled\n ? {\n size: this.totalCompressedSize,\n mimeType: 'audio/webm',\n format: this.recordingConfig.compression.format ?? 'opus',\n bitrate:\n this.recordingConfig.compression.bitrate ?? 128000,\n }\n : undefined,\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 { LegacyEventEmitter } from 'expo-modules-core'\n\nimport { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types'\nimport {\n AudioRecording,\n AudioStreamStatus,\n BitDepth,\n ConsoleLike,\n RecordingConfig,\n StartRecordingResult,\n} from './ExpoAudioStream.types'\nimport { WebRecorder } from './WebRecorder.web'\nimport { AudioEventPayload } from './events'\nimport { encodingToBitDepth } from './utils/encodingToBitDepth'\n\nexport interface EmitAudioEventProps {\n data: Float32Array\n position: number\n compression?: {\n data: Blob\n size: number\n totalSize: number\n mimeType: string\n format: string\n bitrate: number\n }\n}\nexport type EmitAudioEventFunction = (_: EmitAudioEventProps) => void\nexport type EmitAudioAnalysisFunction = (_: AudioAnalysis) => void\n\nexport interface ExpoAudioStreamWebProps {\n logger?: ConsoleLike\n audioWorkletUrl: string\n featuresExtratorUrl: string\n maxBufferSize?: number // Maximum number of chunks to keep in memory\n}\n\nexport class ExpoAudioStreamWeb extends LegacyEventEmitter {\n customRecorder: WebRecorder | null\n audioChunks: Float32Array[]\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 lastEmittedCompressionSize: 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 logger?: ConsoleLike\n latestPosition: number = 0\n totalCompressedSize: number = 0\n private readonly maxBufferSize: number\n\n constructor({\n audioWorkletUrl,\n featuresExtratorUrl,\n logger,\n maxBufferSize = 100, // Default to storing last 100 chunks (1 chunk = 0.5 seconds)\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.logger = logger\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.latestPosition = 0\n this.lastEmittedCompressionSize = 0\n this.streamUuid = null // Initialize UUID on first recording start\n this.audioWorkletUrl = audioWorkletUrl\n this.featuresExtratorUrl = featuresExtratorUrl\n this.maxBufferSize = maxBufferSize\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 this.logger?.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 logger: this.logger,\n audioContext,\n source,\n recordingConfig,\n audioWorkletUrl: this.audioWorkletUrl,\n emitAudioEventCallback: ({\n data,\n position,\n compression,\n }: EmitAudioEventProps) => {\n // Keep only the latest chunks based on maxBufferSize\n this.audioChunks.push(new Float32Array(data))\n if (this.audioChunks.length > this.maxBufferSize) {\n this.audioChunks.shift() // Remove oldest chunk\n }\n this.currentSize += data.byteLength\n this.emitAudioEvent({ data, position, compression })\n this.lastEmittedTime = Date.now()\n this.lastEmittedSize = this.currentSize\n this.lastEmittedCompressionSize = compression?.size ?? 0\n },\n emitAudioAnalysisCallback: (audioAnalysisData: AudioAnalysis) => {\n this.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.isPaused = false\n this.lastEmittedSize = 0\n this.lastEmittedTime = 0\n this.lastEmittedCompressionSize = 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 compression: recordingConfig.compression\n ? {\n ...recordingConfig.compression,\n bitrate: recordingConfig.compression?.bitrate ?? 128000,\n size: 0,\n mimeType: 'audio/webm',\n format: recordingConfig.compression?.format ?? 'opus',\n compressedFileUri: '',\n }\n : undefined,\n }\n return streamConfig\n }\n\n emitAudioEvent({ data, position, compression }: EmitAudioEventProps) {\n const fileUri = `${this.streamUuid}.${this.extension}`\n if (compression?.size) {\n this.lastEmittedCompressionSize = compression.size\n this.totalCompressedSize = compression.totalSize\n }\n this.latestPosition = position\n this.currentDurationMs = position * 1000 // Convert position (in seconds) to ms\n\n const audioEventPayload: AudioEventPayload = {\n fileUri,\n mimeType: `audio/${this.extension}`,\n lastEmittedSize: this.lastEmittedSize,\n deltaSize: data.byteLength,\n position,\n totalSize: this.currentSize,\n buffer: data,\n streamUuid: this.streamUuid ?? '',\n compression: compression\n ? {\n data: compression?.data,\n totalSize: this.totalCompressedSize,\n eventDataSize: compression?.size ?? 0,\n position,\n }\n : undefined,\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 this.logger?.debug('[Stop] Starting stop process')\n const startTime = performance.now()\n\n try {\n this.logger?.debug('[Stop] Stopping recorder')\n const { compressedBlob } = await this.customRecorder.stop()\n\n this.isRecording = false\n this.isPaused = false\n this.currentDurationMs = Date.now() - this.recordingStartTime\n\n let compression: AudioRecording['compression']\n let fileUri = `${this.streamUuid}.${this.extension}`\n let mimeType = `audio/${this.extension}`\n\n if (compressedBlob && this.recordingConfig?.compression?.enabled) {\n const compressedUri = URL.createObjectURL(compressedBlob)\n compression = {\n compressedFileUri: compressedUri,\n size: compressedBlob.size,\n mimeType: 'audio/webm',\n format: 'opus',\n bitrate: this.recordingConfig.compression.bitrate ?? 128000,\n }\n // Use compressed values when compression is enabled\n fileUri = compressedUri\n mimeType = 'audio/webm'\n }\n\n this.logger?.debug(\n `[Stop] Completed stop process in ${performance.now() - startTime}ms`,\n {\n durationMs: this.currentDurationMs,\n compressedSize: compression?.size,\n }\n )\n\n return {\n fileUri,\n filename: `${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,\n compression,\n }\n } catch (error) {\n this.logger?.error('[Stop] Error stopping recording:', error)\n throw error\n }\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: this.currentDurationMs,\n size: this.currentSize,\n interval: this.currentInterval,\n mimeType: `audio/${this.extension}`,\n compression: this.recordingConfig?.compression?.enabled\n ? {\n size: this.totalCompressedSize,\n mimeType: 'audio/webm',\n format: this.recordingConfig.compression.format ?? 'opus',\n bitrate:\n this.recordingConfig.compression.bitrate ?? 128000,\n }\n : undefined,\n }\n return status\n }\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types';
|
|
2
|
-
import { ConsoleLike, RecordingConfig
|
|
2
|
+
import { ConsoleLike, RecordingConfig } from './ExpoAudioStream.types';
|
|
3
3
|
import { EmitAudioAnalysisFunction, EmitAudioEventFunction } from './ExpoAudioStream.web';
|
|
4
4
|
interface AudioFeaturesEvent {
|
|
5
5
|
data: {
|
|
@@ -20,8 +20,6 @@ export declare class WebRecorder {
|
|
|
20
20
|
private numberOfChannels;
|
|
21
21
|
private bitDepth;
|
|
22
22
|
private exportBitDepth;
|
|
23
|
-
private audioBuffer;
|
|
24
|
-
private audioBufferSize;
|
|
25
23
|
private audioAnalysisData;
|
|
26
24
|
private packetCount;
|
|
27
25
|
private logger?;
|
|
@@ -29,7 +27,7 @@ export declare class WebRecorder {
|
|
|
29
27
|
private compressedChunks;
|
|
30
28
|
private compressedSize;
|
|
31
29
|
private pendingCompressedChunk;
|
|
32
|
-
private
|
|
30
|
+
private readonly wavMimeType;
|
|
33
31
|
constructor({ audioContext, source, recordingConfig, audioWorkletUrl, emitAudioEventCallback, emitAudioAnalysisCallback, logger, }: {
|
|
34
32
|
audioContext: AudioContext;
|
|
35
33
|
source: MediaStreamAudioSourceNode;
|
|
@@ -45,10 +43,14 @@ export declare class WebRecorder {
|
|
|
45
43
|
handleWorkerError(error: ErrorEvent): void;
|
|
46
44
|
handleFeatureExtractorMessage(event: AudioFeaturesEvent): void;
|
|
47
45
|
start(): void;
|
|
48
|
-
stop(
|
|
46
|
+
stop(): Promise<{
|
|
49
47
|
pcmData: Float32Array;
|
|
50
48
|
compressedBlob?: Blob;
|
|
51
49
|
}>;
|
|
50
|
+
private cleanup;
|
|
51
|
+
private processRecordingStop;
|
|
52
|
+
private stopCompressedRecording;
|
|
53
|
+
private stopAudioWorklet;
|
|
52
54
|
pause(): void;
|
|
53
55
|
stopMediaStreamTracks(): void;
|
|
54
56
|
playRecordedData({ recordedData, }: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebRecorder.web.d.ts","sourceRoot":"","sources":["../src/WebRecorder.web.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AACnE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"WebRecorder.web.d.ts","sourceRoot":"","sources":["../src/WebRecorder.web.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AACnE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACtE,OAAO,EACH,yBAAyB,EACzB,sBAAsB,EACzB,MAAM,uBAAuB,CAAA;AAe9B,UAAU,kBAAkB;IACxB,IAAI,EAAE;QACF,OAAO,EAAE,MAAM,CAAA;QACf,MAAM,EAAE,aAAa,CAAA;KACxB,CAAA;CACJ;AAkBD,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,CAAY;IAC5B,OAAO,CAAC,gBAAgB,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,iBAAiB,CAAe;IACxC,OAAO,CAAC,WAAW,CAAY;IAC/B,OAAO,CAAC,MAAM,CAAC,CAAa;IAC5B,OAAO,CAAC,uBAAuB,CAA6B;IAC5D,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,cAAc,CAAY;IAClC,OAAO,CAAC,sBAAsB,CAAoB;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;gBAE9B,EACR,YAAY,EACZ,MAAM,EACN,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,yBAAyB,EACzB,MAAM,GACT,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;QACpD,MAAM,CAAC,EAAE,WAAW,CAAA;KACvB;IAqDK,IAAI;IA8GV,0BAA0B,CAAC,mBAAmB,CAAC,EAAE,MAAM;IA0BvD,kBAAkB;IAqBlB,iBAAiB,CAAC,KAAK,EAAE,UAAU;IAInC,6BAA6B,CAAC,KAAK,EAAE,kBAAkB;IAgCvD,KAAK;IAUC,IAAI,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,YAAY,CAAC;QAAC,cAAc,CAAC,EAAE,IAAI,CAAA;KAAE,CAAC;IAiBvE,OAAO,CAAC,OAAO;YAcD,oBAAoB;IAwBlC,OAAO,CAAC,uBAAuB;IA0B/B,OAAO,CAAC,gBAAgB;IAwDxB,KAAK;IAOL,qBAAqB;IAMf,gBAAgB,CAAC,EACnB,YAAY,GACf,EAAE;QACC,YAAY,EAAE,WAAW,CAAA;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;KACpB;IAiCD,OAAO,CAAC,uBAAuB;IAoB/B,MAAM;IAON,OAAO,CAAC,4BAA4B;CAiCvC"}
|