@siteed/expo-audio-stream 1.7.2 → 1.8.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 +17 -1
- package/README.md +6 -1
- package/android/src/main/java/net/siteed/audiostream/AudioFileHandler.kt +39 -0
- package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +124 -12
- package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +26 -2
- package/build/AudioRecorder.provider.d.ts.map +1 -1
- package/build/AudioRecorder.provider.js +1 -0
- package/build/AudioRecorder.provider.js.map +1 -1
- package/build/ExpoAudioStream.types.d.ts +35 -1
- package/build/ExpoAudioStream.types.d.ts.map +1 -1
- package/build/ExpoAudioStream.types.js.map +1 -1
- package/build/ExpoAudioStream.web.d.ts +14 -3
- package/build/ExpoAudioStream.web.d.ts.map +1 -1
- package/build/ExpoAudioStream.web.js +102 -38
- package/build/ExpoAudioStream.web.js.map +1 -1
- package/build/WebRecorder.web.d.ts +11 -2
- package/build/WebRecorder.web.d.ts.map +1 -1
- package/build/WebRecorder.web.js +178 -43
- package/build/WebRecorder.web.js.map +1 -1
- package/build/events.d.ts +6 -0
- package/build/events.d.ts.map +1 -1
- package/build/events.js.map +1 -1
- package/build/useAudioRecorder.d.ts +3 -2
- package/build/useAudioRecorder.d.ts.map +1 -1
- package/build/useAudioRecorder.js +46 -5
- package/build/useAudioRecorder.js.map +1 -1
- package/ios/AudioStreamManager.swift +127 -8
- package/ios/AudioStreamManagerDelegate.swift +8 -2
- package/ios/ExpoAudioStreamModule.swift +61 -46
- package/ios/RecordingResult.swift +2 -0
- package/ios/RecordingSettings.swift +63 -3
- package/package.json +1 -1
- package/src/AudioRecorder.provider.tsx +1 -0
- package/src/ExpoAudioStream.types.ts +38 -1
- package/src/ExpoAudioStream.web.ts +114 -38
- package/src/WebRecorder.web.ts +210 -64
- package/src/events.ts +7 -0
- package/src/useAudioRecorder.tsx +70 -8
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { LegacyEventEmitter } from 'expo-modules-core';
|
|
2
2
|
import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types';
|
|
3
|
-
import { AudioRecording, AudioStreamStatus, BitDepth, ConsoleLike, RecordingConfig, StartRecordingResult } from './ExpoAudioStream.types';
|
|
3
|
+
import { AudioRecording, AudioStreamStatus, BitDepth, ConsoleLike, RecordingConfig, StartRecordingResult, WebRecordingOptions } from './ExpoAudioStream.types';
|
|
4
4
|
import { WebRecorder } from './WebRecorder.web';
|
|
5
5
|
export interface EmitAudioEventProps {
|
|
6
6
|
data: Float32Array;
|
|
7
7
|
position: number;
|
|
8
|
+
compression?: {
|
|
9
|
+
data: Blob;
|
|
10
|
+
size: number;
|
|
11
|
+
totalSize: number;
|
|
12
|
+
mimeType: string;
|
|
13
|
+
format: string;
|
|
14
|
+
bitrate: number;
|
|
15
|
+
};
|
|
8
16
|
}
|
|
9
17
|
export type EmitAudioEventFunction = (_: EmitAudioEventProps) => void;
|
|
10
18
|
export type EmitAudioAnalysisFunction = (_: AudioAnalysis) => void;
|
|
@@ -25,6 +33,7 @@ export declare class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
25
33
|
currentInterval: number;
|
|
26
34
|
lastEmittedSize: number;
|
|
27
35
|
lastEmittedTime: number;
|
|
36
|
+
lastEmittedCompressionSize: number;
|
|
28
37
|
streamUuid: string | null;
|
|
29
38
|
extension: 'webm' | 'wav';
|
|
30
39
|
recordingConfig?: RecordingConfig;
|
|
@@ -32,11 +41,13 @@ export declare class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
32
41
|
audioWorkletUrl: string;
|
|
33
42
|
featuresExtratorUrl: string;
|
|
34
43
|
logger?: ConsoleLike;
|
|
44
|
+
latestPosition: number;
|
|
45
|
+
totalCompressedSize: number;
|
|
35
46
|
constructor({ audioWorkletUrl, featuresExtratorUrl, logger, }: ExpoAudioStreamWebProps);
|
|
36
47
|
getMediaStream(): Promise<MediaStream>;
|
|
37
48
|
startRecording(recordingConfig?: RecordingConfig): Promise<StartRecordingResult>;
|
|
38
|
-
emitAudioEvent({ data, position }: EmitAudioEventProps): void;
|
|
39
|
-
stopRecording(): Promise<AudioRecording>;
|
|
49
|
+
emitAudioEvent({ data, position, compression }: EmitAudioEventProps): void;
|
|
50
|
+
stopRecording(options?: WebRecordingOptions): Promise<AudioRecording>;
|
|
40
51
|
pauseRecording(): Promise<void>;
|
|
41
52
|
resumeRecording(): Promise<void>;
|
|
42
53
|
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,EACpB,mBAAmB,EACtB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAK/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;CAC9B;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;gBAEnB,EACR,eAAe,EACf,mBAAmB,EACnB,MAAM,GACT,EAAE,uBAAuB;IAgCpB,cAAc;IAUd,cAAc,CAAC,eAAe,GAAE,eAAoB;IAgF1D,cAAc,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,mBAAmB;IAgC7D,aAAa,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,CAAC;IAqErE,cAAc;IAad,eAAe;IAarB,MAAM;CAoBT"}
|
|
@@ -15,6 +15,7 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
15
15
|
currentInterval;
|
|
16
16
|
lastEmittedSize;
|
|
17
17
|
lastEmittedTime;
|
|
18
|
+
lastEmittedCompressionSize;
|
|
18
19
|
streamUuid;
|
|
19
20
|
extension = 'wav'; // Default extension is 'webm'
|
|
20
21
|
recordingConfig;
|
|
@@ -22,6 +23,8 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
22
23
|
audioWorkletUrl;
|
|
23
24
|
featuresExtratorUrl;
|
|
24
25
|
logger;
|
|
26
|
+
latestPosition = 0;
|
|
27
|
+
totalCompressedSize = 0;
|
|
25
28
|
constructor({ audioWorkletUrl, featuresExtratorUrl, logger, }) {
|
|
26
29
|
const mockNativeModule = {
|
|
27
30
|
addListener: () => {
|
|
@@ -45,6 +48,8 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
45
48
|
this.currentInterval = 1000; // Default interval in ms
|
|
46
49
|
this.lastEmittedSize = 0;
|
|
47
50
|
this.lastEmittedTime = 0;
|
|
51
|
+
this.latestPosition = 0;
|
|
52
|
+
this.lastEmittedCompressionSize = 0;
|
|
48
53
|
this.streamUuid = null; // Initialize UUID on first recording start
|
|
49
54
|
this.audioWorkletUrl = audioWorkletUrl;
|
|
50
55
|
this.featuresExtratorUrl = featuresExtratorUrl;
|
|
@@ -55,7 +60,7 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
55
60
|
return await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
56
61
|
}
|
|
57
62
|
catch (error) {
|
|
58
|
-
|
|
63
|
+
this.logger?.error('Failed to get media stream:', error);
|
|
59
64
|
throw error;
|
|
60
65
|
}
|
|
61
66
|
}
|
|
@@ -74,16 +79,18 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
74
79
|
const stream = await this.getMediaStream();
|
|
75
80
|
const source = audioContext.createMediaStreamSource(stream);
|
|
76
81
|
this.customRecorder = new WebRecorder({
|
|
82
|
+
logger: this.logger,
|
|
77
83
|
audioContext,
|
|
78
84
|
source,
|
|
79
85
|
recordingConfig,
|
|
80
86
|
audioWorkletUrl: this.audioWorkletUrl,
|
|
81
|
-
emitAudioEventCallback: ({ data, position, }) => {
|
|
87
|
+
emitAudioEventCallback: ({ data, position, compression, }) => {
|
|
82
88
|
this.audioChunks.push(new Float32Array(data));
|
|
83
89
|
this.currentSize += data.byteLength;
|
|
84
|
-
this.emitAudioEvent({ data, position });
|
|
90
|
+
this.emitAudioEvent({ data, position, compression });
|
|
85
91
|
this.lastEmittedTime = Date.now();
|
|
86
92
|
this.lastEmittedSize = this.currentSize;
|
|
93
|
+
this.lastEmittedCompressionSize = compression?.size ?? 0;
|
|
87
94
|
},
|
|
88
95
|
emitAudioAnalysisCallback: (audioAnalysisData) => {
|
|
89
96
|
this.logger?.log(`Emitted AudioAnalysis:`, audioAnalysisData);
|
|
@@ -105,6 +112,7 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
105
112
|
this.isPaused = false;
|
|
106
113
|
this.lastEmittedSize = 0;
|
|
107
114
|
this.lastEmittedTime = 0;
|
|
115
|
+
this.lastEmittedCompressionSize = 0;
|
|
108
116
|
this.streamUuid = Date.now().toString();
|
|
109
117
|
const fileUri = `${this.streamUuid}.${this.extension}`;
|
|
110
118
|
const streamConfig = {
|
|
@@ -113,59 +121,107 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
113
121
|
bitDepth: this.bitDepth,
|
|
114
122
|
channels: recordingConfig.channels ?? 1,
|
|
115
123
|
sampleRate: recordingConfig.sampleRate ?? 44100,
|
|
124
|
+
compression: recordingConfig.compression
|
|
125
|
+
? {
|
|
126
|
+
...recordingConfig.compression,
|
|
127
|
+
bitrate: recordingConfig.compression?.bitrate ?? 128000,
|
|
128
|
+
size: 0,
|
|
129
|
+
mimeType: 'audio/webm',
|
|
130
|
+
format: recordingConfig.compression?.format ?? 'opus',
|
|
131
|
+
compressedFileUri: '',
|
|
132
|
+
}
|
|
133
|
+
: undefined,
|
|
116
134
|
};
|
|
117
135
|
return streamConfig;
|
|
118
136
|
}
|
|
119
|
-
emitAudioEvent({ data, position }) {
|
|
137
|
+
emitAudioEvent({ data, position, compression }) {
|
|
120
138
|
const fileUri = `${this.streamUuid}.${this.extension}`;
|
|
139
|
+
if (compression?.size) {
|
|
140
|
+
this.lastEmittedCompressionSize = compression.size;
|
|
141
|
+
this.totalCompressedSize = compression.totalSize;
|
|
142
|
+
}
|
|
143
|
+
this.latestPosition = position;
|
|
144
|
+
this.currentDurationMs = position * 1000; // Convert position (in seconds) to ms
|
|
121
145
|
const audioEventPayload = {
|
|
122
146
|
fileUri,
|
|
123
147
|
mimeType: `audio/${this.extension}`,
|
|
124
|
-
lastEmittedSize: this.lastEmittedSize,
|
|
148
|
+
lastEmittedSize: this.lastEmittedSize,
|
|
125
149
|
deltaSize: data.byteLength,
|
|
126
150
|
position,
|
|
127
151
|
totalSize: this.currentSize,
|
|
128
152
|
buffer: data,
|
|
129
|
-
streamUuid: this.streamUuid ?? '',
|
|
153
|
+
streamUuid: this.streamUuid ?? '',
|
|
154
|
+
compression: compression
|
|
155
|
+
? {
|
|
156
|
+
data: compression?.data,
|
|
157
|
+
totalSize: this.totalCompressedSize,
|
|
158
|
+
eventDataSize: compression?.size ?? 0,
|
|
159
|
+
position,
|
|
160
|
+
}
|
|
161
|
+
: undefined,
|
|
130
162
|
};
|
|
131
163
|
this.emit('AudioData', audioEventPayload);
|
|
132
164
|
}
|
|
133
165
|
// Stop recording
|
|
134
|
-
async stopRecording() {
|
|
166
|
+
async stopRecording(options) {
|
|
135
167
|
if (!this.customRecorder) {
|
|
136
168
|
throw new Error('Recorder is not initialized');
|
|
137
169
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
170
|
+
// Create a promise to handle the PCM data processing
|
|
171
|
+
return new Promise((resolve) => {
|
|
172
|
+
// Use requestAnimationFrame to avoid blocking the UI
|
|
173
|
+
requestAnimationFrame(() => {
|
|
174
|
+
// Move the async work inside a self-executing async function
|
|
175
|
+
(async () => {
|
|
176
|
+
const { pcmData, compressedBlob } = await this.customRecorder.stop(options);
|
|
177
|
+
this.logger?.debug(`Stopped recording`, pcmData);
|
|
178
|
+
this.isRecording = false;
|
|
179
|
+
this.isPaused = false;
|
|
180
|
+
this.currentDurationMs = Date.now() - this.recordingStartTime;
|
|
181
|
+
// Process in the next frame to avoid blocking
|
|
182
|
+
requestAnimationFrame(() => {
|
|
183
|
+
// Rest of the code remains the same
|
|
184
|
+
const wavBuffer = writeWavHeader({
|
|
185
|
+
buffer: pcmData.buffer,
|
|
186
|
+
sampleRate: this.recordingConfig?.sampleRate ?? 44100,
|
|
187
|
+
numChannels: this.recordingConfig?.channels ?? 1,
|
|
188
|
+
bitDepth: this.bitDepth,
|
|
189
|
+
});
|
|
190
|
+
const cloneableBuffer = wavBuffer.slice(0);
|
|
191
|
+
const blob = new Blob([cloneableBuffer], {
|
|
192
|
+
type: `audio/${this.extension}`,
|
|
193
|
+
});
|
|
194
|
+
const fileUri = URL.createObjectURL(blob);
|
|
195
|
+
let compression;
|
|
196
|
+
if (compressedBlob && this.recordingConfig?.compression?.enabled) {
|
|
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
|
+
});
|
|
223
|
+
});
|
|
155
224
|
});
|
|
156
|
-
const fileUri = URL.createObjectURL(blob);
|
|
157
|
-
const result = {
|
|
158
|
-
fileUri,
|
|
159
|
-
filename: `${this.streamUuid}.${this.extension}`,
|
|
160
|
-
wavPCMData: new Float32Array(cloneableBuffer),
|
|
161
|
-
bitDepth: this.bitDepth,
|
|
162
|
-
channels: this.recordingConfig?.channels ?? 1,
|
|
163
|
-
sampleRate: this.recordingConfig?.sampleRate ?? 44100,
|
|
164
|
-
durationMs: this.currentDurationMs,
|
|
165
|
-
size: this.currentSize,
|
|
166
|
-
mimeType: `audio/${this.extension}`,
|
|
167
|
-
};
|
|
168
|
-
return result;
|
|
169
225
|
}
|
|
170
226
|
// Pause recording
|
|
171
227
|
async pauseRecording() {
|
|
@@ -194,10 +250,18 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
194
250
|
const status = {
|
|
195
251
|
isRecording: this.isRecording,
|
|
196
252
|
isPaused: this.isPaused,
|
|
197
|
-
durationMs:
|
|
253
|
+
durationMs: this.currentDurationMs,
|
|
198
254
|
size: this.currentSize,
|
|
199
255
|
interval: this.currentInterval,
|
|
200
256
|
mimeType: `audio/${this.extension}`,
|
|
257
|
+
compression: this.recordingConfig?.compression?.enabled
|
|
258
|
+
? {
|
|
259
|
+
size: this.totalCompressedSize,
|
|
260
|
+
mimeType: 'audio/webm',
|
|
261
|
+
format: this.recordingConfig.compression.format ?? 'opus',
|
|
262
|
+
bitrate: this.recordingConfig.compression.bitrate ?? 128000,
|
|
263
|
+
}
|
|
264
|
+
: undefined,
|
|
201
265
|
};
|
|
202
266
|
return status;
|
|
203
267
|
}
|
|
@@ -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} 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}\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 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\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.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(new Float32Array(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 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.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 this.logger?.debug(`Stopped recording`, fullPcmBufferArray)\n this.isRecording = false\n this.isPaused = false\n this.currentDurationMs = Date.now() - this.recordingStartTime\n\n // Create WAV header and combine with PCM data in one step\n const wavBuffer = writeWavHeader({\n buffer: fullPcmBufferArray.buffer,\n sampleRate: this.recordingConfig?.sampleRate ?? 44100,\n numChannels: this.recordingConfig?.channels ?? 1,\n bitDepth: this.bitDepth,\n })\n\n // Create a cloneable copy\n const cloneableBuffer = wavBuffer.slice(0)\n\n // Create blob with complete WAV data\n const blob = new Blob([cloneableBuffer], {\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: 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 }\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 { 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,5 +1,5 @@
|
|
|
1
1
|
import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types';
|
|
2
|
-
import { ConsoleLike, RecordingConfig } from './ExpoAudioStream.types';
|
|
2
|
+
import { ConsoleLike, RecordingConfig, WebRecordingOptions } from './ExpoAudioStream.types';
|
|
3
3
|
import { EmitAudioAnalysisFunction, EmitAudioEventFunction } from './ExpoAudioStream.web';
|
|
4
4
|
interface AudioFeaturesEvent {
|
|
5
5
|
data: {
|
|
@@ -25,6 +25,11 @@ export declare class WebRecorder {
|
|
|
25
25
|
private audioAnalysisData;
|
|
26
26
|
private packetCount;
|
|
27
27
|
private logger?;
|
|
28
|
+
private compressedMediaRecorder;
|
|
29
|
+
private compressedChunks;
|
|
30
|
+
private compressedSize;
|
|
31
|
+
private pendingCompressedChunk;
|
|
32
|
+
private audioChunks;
|
|
28
33
|
constructor({ audioContext, source, recordingConfig, audioWorkletUrl, emitAudioEventCallback, emitAudioAnalysisCallback, logger, }: {
|
|
29
34
|
audioContext: AudioContext;
|
|
30
35
|
source: MediaStreamAudioSourceNode;
|
|
@@ -40,7 +45,10 @@ export declare class WebRecorder {
|
|
|
40
45
|
handleWorkerError(error: ErrorEvent): void;
|
|
41
46
|
handleFeatureExtractorMessage(event: AudioFeaturesEvent): void;
|
|
42
47
|
start(): void;
|
|
43
|
-
stop(): Promise<
|
|
48
|
+
stop(options?: WebRecordingOptions): Promise<{
|
|
49
|
+
pcmData: Float32Array;
|
|
50
|
+
compressedBlob?: Blob;
|
|
51
|
+
}>;
|
|
44
52
|
pause(): void;
|
|
45
53
|
stopMediaStreamTracks(): void;
|
|
46
54
|
playRecordedData({ recordedData, }: {
|
|
@@ -49,6 +57,7 @@ export declare class WebRecorder {
|
|
|
49
57
|
}): Promise<void>;
|
|
50
58
|
private checkAudioContextFormat;
|
|
51
59
|
resume(): void;
|
|
60
|
+
private initializeCompressedRecorder;
|
|
52
61
|
}
|
|
53
62
|
export {};
|
|
54
63
|
//# sourceMappingURL=WebRecorder.web.d.ts.map
|
|
@@ -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,MAAM,yBAAyB,CAAA;
|
|
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,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAC3F,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;AAUD,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,WAAW,CAAc;IACjC,OAAO,CAAC,eAAe,CAAQ;IAC/B,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,WAAW,CAAqB;gBAE5B,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;IA0DK,IAAI;IA8HV,0BAA0B,CAAC,mBAAmB,CAAC,EAAE,MAAM;IA0BvD,kBAAkB;IAqBlB,iBAAiB,CAAC,KAAK,EAAE,UAAU;IAInC,6BAA6B,CAAC,KAAK,EAAE,kBAAkB;IAgCvD,KAAK;IAUC,IAAI,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,YAAY,CAAC;QAAC,cAAc,CAAC,EAAE,IAAI,CAAA;KAAE,CAAC;IAwKpG,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"}
|