@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/src/WebRecorder.web.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/WebRecorder.ts
|
|
2
2
|
|
|
3
3
|
import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types'
|
|
4
|
-
import { ConsoleLike, RecordingConfig
|
|
4
|
+
import { ConsoleLike, RecordingConfig } from './ExpoAudioStream.types'
|
|
5
5
|
import {
|
|
6
6
|
EmitAudioAnalysisFunction,
|
|
7
7
|
EmitAudioEventFunction,
|
|
@@ -35,6 +35,14 @@ const DEFAULT_ALGORITHM = 'rms'
|
|
|
35
35
|
|
|
36
36
|
const TAG = 'WebRecorder'
|
|
37
37
|
|
|
38
|
+
const STOP_PERFORMANCE_MARKS = {
|
|
39
|
+
STOP_INITIATED: 'stopInitiated',
|
|
40
|
+
COMPRESSED_RECORDING_STOP: 'compressedRecordingStop',
|
|
41
|
+
AUDIO_WORKLET_STOP: 'audioWorkletStop',
|
|
42
|
+
CLEANUP: 'cleanup',
|
|
43
|
+
TOTAL_STOP_TIME: 'totalStopTime',
|
|
44
|
+
} as const
|
|
45
|
+
|
|
38
46
|
export class WebRecorder {
|
|
39
47
|
private audioContext: AudioContext
|
|
40
48
|
private audioWorkletNode!: AudioWorkletNode
|
|
@@ -44,12 +52,10 @@ export class WebRecorder {
|
|
|
44
52
|
private emitAudioEventCallback: EmitAudioEventFunction
|
|
45
53
|
private emitAudioAnalysisCallback: EmitAudioAnalysisFunction
|
|
46
54
|
private config: RecordingConfig
|
|
47
|
-
private position: number
|
|
55
|
+
private position: number = 0
|
|
48
56
|
private numberOfChannels: number // Number of audio channels
|
|
49
57
|
private bitDepth: number // Bit depth of the audio
|
|
50
58
|
private exportBitDepth: number // Bit depth of the audio
|
|
51
|
-
private audioBuffer: Float32Array // Single buffer to store the audio data
|
|
52
|
-
private audioBufferSize: number // Keep track of the buffer size
|
|
53
59
|
private audioAnalysisData: AudioAnalysis // Keep updating the full audio analysis data with latest events
|
|
54
60
|
private packetCount: number = 0
|
|
55
61
|
private logger?: ConsoleLike
|
|
@@ -57,7 +63,7 @@ export class WebRecorder {
|
|
|
57
63
|
private compressedChunks: Blob[] = []
|
|
58
64
|
private compressedSize: number = 0
|
|
59
65
|
private pendingCompressedChunk: Blob | null = null
|
|
60
|
-
private
|
|
66
|
+
private readonly wavMimeType = 'audio/wav'
|
|
61
67
|
|
|
62
68
|
constructor({
|
|
63
69
|
audioContext,
|
|
@@ -82,7 +88,6 @@ export class WebRecorder {
|
|
|
82
88
|
this.emitAudioEventCallback = emitAudioEventCallback
|
|
83
89
|
this.emitAudioAnalysisCallback = emitAudioAnalysisCallback
|
|
84
90
|
this.config = recordingConfig
|
|
85
|
-
this.position = 0
|
|
86
91
|
this.logger = logger
|
|
87
92
|
|
|
88
93
|
const audioContextFormat = this.checkAudioContextFormat({
|
|
@@ -105,10 +110,6 @@ export class WebRecorder {
|
|
|
105
110
|
audioContextFormat.bitDepth ||
|
|
106
111
|
DEFAULT_WEB_BITDEPTH
|
|
107
112
|
|
|
108
|
-
// Initialize the audio buffer separately
|
|
109
|
-
this.audioBuffer = new Float32Array(0)
|
|
110
|
-
this.audioBufferSize = 0
|
|
111
|
-
|
|
112
113
|
this.audioAnalysisData = {
|
|
113
114
|
amplitudeRange: { min: 0, max: 0 },
|
|
114
115
|
dataPoints: [],
|
|
@@ -155,85 +156,69 @@ export class WebRecorder {
|
|
|
155
156
|
event: AudioWorkletEvent
|
|
156
157
|
) => {
|
|
157
158
|
const command = event.data.command
|
|
158
|
-
if (command !== 'newData')
|
|
159
|
-
return
|
|
160
|
-
}
|
|
161
|
-
const pcmBufferFloat = event.data.recordedData
|
|
159
|
+
if (command !== 'newData') return
|
|
162
160
|
|
|
161
|
+
const pcmBufferFloat = event.data.recordedData
|
|
163
162
|
if (!pcmBufferFloat) {
|
|
164
163
|
this.logger?.warn('Received empty audio buffer', event)
|
|
165
164
|
return
|
|
166
165
|
}
|
|
167
166
|
|
|
168
|
-
//
|
|
169
|
-
this.
|
|
170
|
-
this.audioBufferSize += pcmBufferFloat.length
|
|
171
|
-
|
|
167
|
+
// Process data in smaller chunks and emit immediately
|
|
168
|
+
const chunkSize = this.audioContext.sampleRate * 2 // Reduce to 2 seconds chunks
|
|
172
169
|
const sampleRate =
|
|
173
170
|
event.data.sampleRate ?? this.audioContext.sampleRate
|
|
174
|
-
const duration = pcmBufferFloat.length / sampleRate
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if (this.pendingCompressedChunk) {
|
|
203
|
-
compressionData = {
|
|
204
|
-
data: this.pendingCompressedChunk,
|
|
205
|
-
size: this.pendingCompressedChunk.size,
|
|
206
|
-
totalSize: this.compressedSize,
|
|
207
|
-
mimeType: 'audio/webm',
|
|
208
|
-
format: 'opus',
|
|
209
|
-
bitrate: this.config.compression?.bitrate ?? 128000,
|
|
171
|
+
const duration = pcmBufferFloat.length / sampleRate
|
|
172
|
+
|
|
173
|
+
// Emit chunks without storing them
|
|
174
|
+
for (let i = 0; i < pcmBufferFloat.length; i += chunkSize) {
|
|
175
|
+
const chunk = pcmBufferFloat.slice(i, i + chunkSize)
|
|
176
|
+
const chunkPosition = this.position + i / sampleRate
|
|
177
|
+
|
|
178
|
+
// Process features if enabled
|
|
179
|
+
if (
|
|
180
|
+
this.config.enableProcessing &&
|
|
181
|
+
this.featureExtractorWorker
|
|
182
|
+
) {
|
|
183
|
+
this.featureExtractorWorker.postMessage(
|
|
184
|
+
{
|
|
185
|
+
command: 'process',
|
|
186
|
+
channelData: chunk,
|
|
187
|
+
sampleRate,
|
|
188
|
+
pointsPerSecond:
|
|
189
|
+
this.config.pointsPerSecond ||
|
|
190
|
+
DEFAULT_WEB_POINTS_PER_SECOND,
|
|
191
|
+
algorithm: this.config.algorithm || 'rms',
|
|
192
|
+
bitDepth: this.bitDepth,
|
|
193
|
+
fullAudioDurationMs: this.position * 1000,
|
|
194
|
+
numberOfChannels: this.numberOfChannels,
|
|
195
|
+
features: this.config.features,
|
|
196
|
+
},
|
|
197
|
+
[]
|
|
198
|
+
)
|
|
210
199
|
}
|
|
211
|
-
|
|
200
|
+
|
|
201
|
+
// Emit chunk immediately
|
|
202
|
+
this.emitAudioEventCallback({
|
|
203
|
+
data: chunk,
|
|
204
|
+
position: chunkPosition,
|
|
205
|
+
compression: this.pendingCompressedChunk
|
|
206
|
+
? {
|
|
207
|
+
data: this.pendingCompressedChunk,
|
|
208
|
+
size: this.pendingCompressedChunk.size,
|
|
209
|
+
totalSize: this.compressedSize,
|
|
210
|
+
mimeType: 'audio/webm',
|
|
211
|
+
format: 'opus',
|
|
212
|
+
bitrate:
|
|
213
|
+
this.config.compression?.bitrate ??
|
|
214
|
+
128000,
|
|
215
|
+
}
|
|
216
|
+
: undefined,
|
|
217
|
+
})
|
|
212
218
|
}
|
|
213
219
|
|
|
214
|
-
this.
|
|
215
|
-
|
|
216
|
-
position: this.position,
|
|
217
|
-
compression: compressionData,
|
|
218
|
-
})
|
|
219
|
-
this.position += duration // Update position
|
|
220
|
-
|
|
221
|
-
this.featureExtractorWorker?.postMessage(
|
|
222
|
-
{
|
|
223
|
-
command: 'process',
|
|
224
|
-
channelData: pcmBufferFloat,
|
|
225
|
-
sampleRate,
|
|
226
|
-
pointsPerSecond:
|
|
227
|
-
this.config.pointsPerSecond ||
|
|
228
|
-
DEFAULT_WEB_POINTS_PER_SECOND,
|
|
229
|
-
algorithm: this.config.algorithm || 'rms',
|
|
230
|
-
bitDepth: this.bitDepth,
|
|
231
|
-
fullAudioDurationMs: this.position * 1000,
|
|
232
|
-
numberOfChannels: this.numberOfChannels,
|
|
233
|
-
features: this.config.features,
|
|
234
|
-
},
|
|
235
|
-
[]
|
|
236
|
-
)
|
|
220
|
+
this.position += duration
|
|
221
|
+
this.pendingCompressedChunk = null
|
|
237
222
|
}
|
|
238
223
|
|
|
239
224
|
this.logger?.debug(
|
|
@@ -242,7 +227,7 @@ export class WebRecorder {
|
|
|
242
227
|
)
|
|
243
228
|
this.audioWorkletNode.port.postMessage({
|
|
244
229
|
command: 'init',
|
|
245
|
-
recordSampleRate: this.audioContext.sampleRate,
|
|
230
|
+
recordSampleRate: this.audioContext.sampleRate,
|
|
246
231
|
exportSampleRate:
|
|
247
232
|
this.config.sampleRate ?? this.audioContext.sampleRate,
|
|
248
233
|
bitDepth: this.bitDepth,
|
|
@@ -352,171 +337,140 @@ export class WebRecorder {
|
|
|
352
337
|
}
|
|
353
338
|
}
|
|
354
339
|
|
|
355
|
-
async stop(
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
this.audioContext.close()
|
|
365
|
-
}
|
|
366
|
-
if (this.audioWorkletNode) {
|
|
367
|
-
this.audioWorkletNode.disconnect()
|
|
368
|
-
}
|
|
369
|
-
if (this.source) {
|
|
370
|
-
this.source.disconnect()
|
|
340
|
+
async stop(): Promise<{ pcmData: Float32Array; compressedBlob?: Blob }> {
|
|
341
|
+
try {
|
|
342
|
+
if (this.compressedMediaRecorder) {
|
|
343
|
+
this.compressedMediaRecorder.stop()
|
|
344
|
+
return {
|
|
345
|
+
pcmData: new Float32Array(), // Return empty array since we're streaming
|
|
346
|
+
compressedBlob: new Blob(this.compressedChunks, {
|
|
347
|
+
type: 'audio/webm;codecs=opus',
|
|
348
|
+
}),
|
|
371
349
|
}
|
|
372
|
-
|
|
373
|
-
this.stopMediaStreamTracks()
|
|
374
350
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
this.compressedMediaRecorder.onstop = () => {
|
|
381
|
-
const compressedBlob = new Blob(this.compressedChunks, {
|
|
382
|
-
type: 'audio/webm;codecs=opus',
|
|
383
|
-
})
|
|
384
|
-
cleanup()
|
|
385
|
-
// Return the last chunk as pcmData to maintain interface compatibility
|
|
386
|
-
resolve({
|
|
387
|
-
pcmData: this.audioChunks[this.audioChunks.length - 1] || new Float32Array(),
|
|
388
|
-
compressedBlob,
|
|
389
|
-
})
|
|
390
|
-
}
|
|
391
|
-
this.compressedMediaRecorder.stop()
|
|
392
|
-
return
|
|
393
|
-
}
|
|
351
|
+
return { pcmData: new Float32Array() }
|
|
352
|
+
} finally {
|
|
353
|
+
this.cleanup()
|
|
354
|
+
}
|
|
355
|
+
}
|
|
394
356
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
357
|
+
private cleanup() {
|
|
358
|
+
if (this.audioContext) {
|
|
359
|
+
this.audioContext.close()
|
|
360
|
+
}
|
|
361
|
+
if (this.audioWorkletNode) {
|
|
362
|
+
this.audioWorkletNode.disconnect()
|
|
363
|
+
}
|
|
364
|
+
if (this.source) {
|
|
365
|
+
this.source.disconnect()
|
|
366
|
+
}
|
|
367
|
+
this.stopMediaStreamTracks()
|
|
368
|
+
}
|
|
401
369
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
370
|
+
// Helper method to process recording stop
|
|
371
|
+
private async processRecordingStop(): Promise<{
|
|
372
|
+
pcmData: Float32Array
|
|
373
|
+
compressedBlob?: Blob
|
|
374
|
+
}> {
|
|
375
|
+
const processStartTime = performance.now()
|
|
376
|
+
this.logger?.debug('[Performance] Starting recording stop process')
|
|
377
|
+
|
|
378
|
+
const [compressedData, workletData] = await Promise.all([
|
|
379
|
+
this.stopCompressedRecording(),
|
|
380
|
+
this.stopAudioWorklet(),
|
|
381
|
+
])
|
|
382
|
+
|
|
383
|
+
this.logger?.debug(
|
|
384
|
+
`[Performance] Recording stop process completed in ${performance.now() - processStartTime}ms`
|
|
385
|
+
)
|
|
386
|
+
return {
|
|
387
|
+
pcmData:
|
|
388
|
+
workletData ??
|
|
389
|
+
new Float32Array(this.audioAnalysisData.dataPoints.length),
|
|
390
|
+
compressedBlob: compressedData,
|
|
391
|
+
}
|
|
392
|
+
}
|
|
408
393
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
394
|
+
// Helper method to stop compressed recording
|
|
395
|
+
private stopCompressedRecording(): Promise<Blob | undefined> {
|
|
396
|
+
const startTime = performance.now()
|
|
397
|
+
this.logger?.debug(
|
|
398
|
+
`[Performance][${STOP_PERFORMANCE_MARKS.COMPRESSED_RECORDING_STOP}] Starting compressed recording stop`
|
|
399
|
+
)
|
|
412
400
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
for (let i = chunkIndex; i < endIndex; i++) {
|
|
418
|
-
const chunk = this.audioChunks[i]
|
|
419
|
-
combinedBuffer.set(chunk, offset)
|
|
420
|
-
offset += chunk.length
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
chunkIndex = endIndex
|
|
424
|
-
|
|
425
|
-
if (chunkIndex < this.audioChunks.length) {
|
|
426
|
-
// Process next batch in next frame
|
|
427
|
-
requestAnimationFrame(processNextBatch)
|
|
428
|
-
} else {
|
|
429
|
-
// All chunks processed
|
|
430
|
-
this.audioBuffer = combinedBuffer
|
|
431
|
-
queueMicrotask(() => {
|
|
432
|
-
cleanup()
|
|
433
|
-
resolve({
|
|
434
|
-
pcmData: this.audioBuffer,
|
|
435
|
-
compressedBlob,
|
|
436
|
-
})
|
|
437
|
-
})
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
processNextBatch()
|
|
442
|
-
})
|
|
443
|
-
}
|
|
444
|
-
this.compressedMediaRecorder.stop()
|
|
445
|
-
}
|
|
401
|
+
if (!this.compressedMediaRecorder) {
|
|
402
|
+
this.logger?.debug('[Performance] No compressed recorder to stop')
|
|
403
|
+
return Promise.resolve(undefined)
|
|
404
|
+
}
|
|
446
405
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
406
|
+
return new Promise((resolve) => {
|
|
407
|
+
this.compressedMediaRecorder!.onstop = () => {
|
|
408
|
+
const blob = new Blob(this.compressedChunks, {
|
|
409
|
+
type: 'audio/webm;codecs=opus',
|
|
410
|
+
})
|
|
411
|
+
this.logger?.debug(
|
|
412
|
+
`[Performance][${STOP_PERFORMANCE_MARKS.COMPRESSED_RECORDING_STOP}] Compressed recording stopped in ${performance.now() - startTime}ms, size: ${blob.size}`
|
|
413
|
+
)
|
|
414
|
+
resolve(blob)
|
|
415
|
+
}
|
|
416
|
+
this.compressedMediaRecorder!.stop()
|
|
417
|
+
})
|
|
418
|
+
}
|
|
455
419
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
return
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Convert PCM data if needed based on bit depth
|
|
471
|
-
if (this.exportBitDepth !== this.bitDepth) {
|
|
472
|
-
const convertedData =
|
|
473
|
-
await convertPCMToFloat32({
|
|
474
|
-
buffer: rawPCMDataFull.buffer,
|
|
475
|
-
bitDepth: this.exportBitDepth,
|
|
476
|
-
skipWavHeader: true,
|
|
477
|
-
logger: this.logger,
|
|
478
|
-
})
|
|
479
|
-
this.audioBuffer =
|
|
480
|
-
convertedData.pcmValues
|
|
481
|
-
} else {
|
|
482
|
-
this.audioBuffer = rawPCMDataFull
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
if (!this.compressedMediaRecorder) {
|
|
486
|
-
cleanup()
|
|
487
|
-
resolve({ pcmData: this.audioBuffer })
|
|
488
|
-
}
|
|
489
|
-
} catch (error) {
|
|
490
|
-
cleanup()
|
|
491
|
-
reject(error)
|
|
492
|
-
}
|
|
493
|
-
})
|
|
494
|
-
}
|
|
495
|
-
}
|
|
420
|
+
// Helper method to stop audio worklet
|
|
421
|
+
private stopAudioWorklet(): Promise<Float32Array | undefined> {
|
|
422
|
+
const startTime = performance.now()
|
|
423
|
+
this.logger?.debug(
|
|
424
|
+
`[Performance][${STOP_PERFORMANCE_MARKS.AUDIO_WORKLET_STOP}] Starting audio worklet stop`
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
if (!this.audioWorkletNode) {
|
|
428
|
+
this.logger?.debug('[Performance] No audio worklet to stop')
|
|
429
|
+
return Promise.resolve(undefined)
|
|
430
|
+
}
|
|
496
431
|
|
|
497
|
-
|
|
432
|
+
return new Promise((resolve) => {
|
|
433
|
+
const onMessage = (event: AudioWorkletEvent) => {
|
|
434
|
+
if (event.data.command === 'recordedData') {
|
|
435
|
+
this.audioWorkletNode?.port.removeEventListener(
|
|
498
436
|
'message',
|
|
499
437
|
onMessage
|
|
500
438
|
)
|
|
501
|
-
|
|
439
|
+
const rawPCMDataFull = event.data.recordedData?.slice(0)
|
|
440
|
+
|
|
441
|
+
if (!rawPCMDataFull) {
|
|
442
|
+
this.logger?.debug('[Performance] No PCM data received')
|
|
443
|
+
resolve(undefined)
|
|
444
|
+
return
|
|
445
|
+
}
|
|
502
446
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
447
|
+
if (this.exportBitDepth !== this.bitDepth) {
|
|
448
|
+
const conversionStart = performance.now()
|
|
449
|
+
convertPCMToFloat32({
|
|
450
|
+
buffer: rawPCMDataFull.buffer,
|
|
451
|
+
bitDepth: this.exportBitDepth,
|
|
452
|
+
skipWavHeader: true,
|
|
453
|
+
logger: this.logger,
|
|
454
|
+
}).then(({ pcmValues }) => {
|
|
455
|
+
this.logger?.debug(
|
|
456
|
+
`[Performance] PCM conversion completed in ${performance.now() - conversionStart}ms`
|
|
457
|
+
)
|
|
458
|
+
this.logger?.debug(
|
|
459
|
+
`[Performance][${STOP_PERFORMANCE_MARKS.AUDIO_WORKLET_STOP}] Audio worklet stopped in ${performance.now() - startTime}ms`
|
|
460
|
+
)
|
|
461
|
+
resolve(pcmValues)
|
|
462
|
+
})
|
|
463
|
+
} else {
|
|
464
|
+
this.logger?.debug(
|
|
465
|
+
`[Performance][${STOP_PERFORMANCE_MARKS.AUDIO_WORKLET_STOP}] Audio worklet stopped in ${performance.now() - startTime}ms`
|
|
508
466
|
)
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
}, 5000)
|
|
512
|
-
} else if (!this.compressedMediaRecorder) {
|
|
513
|
-
cleanup()
|
|
514
|
-
resolve({ pcmData: this.audioBuffer })
|
|
467
|
+
resolve(rawPCMDataFull)
|
|
468
|
+
}
|
|
515
469
|
}
|
|
516
|
-
} catch (error) {
|
|
517
|
-
cleanup()
|
|
518
|
-
reject(error)
|
|
519
470
|
}
|
|
471
|
+
|
|
472
|
+
this.audioWorkletNode.port.addEventListener('message', onMessage)
|
|
473
|
+
this.audioWorkletNode.port.postMessage({ command: 'stop' })
|
|
520
474
|
})
|
|
521
475
|
}
|
|
522
476
|
|
package/src/useAudioRecorder.tsx
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
ConsoleLike,
|
|
12
12
|
RecordingConfig,
|
|
13
13
|
StartRecordingResult,
|
|
14
|
-
WebRecordingOptions,
|
|
15
14
|
} from './ExpoAudioStream.types'
|
|
16
15
|
import ExpoAudioStreamModule from './ExpoAudioStreamModule'
|
|
17
16
|
import {
|
|
@@ -28,7 +27,7 @@ export interface UseAudioRecorderProps {
|
|
|
28
27
|
|
|
29
28
|
export interface UseAudioRecorderState {
|
|
30
29
|
startRecording: (_: RecordingConfig) => Promise<StartRecordingResult>
|
|
31
|
-
stopRecording: (
|
|
30
|
+
stopRecording: () => Promise<AudioRecording>
|
|
32
31
|
pauseRecording: () => Promise<void>
|
|
33
32
|
resumeRecording: () => Promise<void>
|
|
34
33
|
isRecording: boolean
|