@siteed/audio-studio 3.2.0 → 3.2.1-beta.1

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.
Files changed (58) hide show
  1. package/README.md +30 -1
  2. package/android/src/main/java/net/siteed/audiostudio/AudioRecorderManager.kt +142 -12
  3. package/android/src/main/java/net/siteed/audiostudio/AudioRecordingService.kt +1 -1
  4. package/android/src/main/java/net/siteed/audiostudio/AudioStudioModule.kt +5 -4
  5. package/android/src/main/java/net/siteed/audiostudio/Constants.kt +2 -1
  6. package/android/src/main/java/net/siteed/audiostudio/RecordingActionReceiver.kt +1 -1
  7. package/android/src/main/java/net/siteed/audiostudio/RecordingConfig.kt +5 -1
  8. package/build/cjs/AudioRecorder.provider.js +3 -37
  9. package/build/cjs/AudioRecorder.provider.js.map +1 -1
  10. package/build/cjs/AudioStudio.types.js.map +1 -1
  11. package/build/cjs/AudioStudio.web.js +125 -13
  12. package/build/cjs/AudioStudio.web.js.map +1 -1
  13. package/build/cjs/AudioStudioModule.js +6 -1
  14. package/build/cjs/AudioStudioModule.js.map +1 -1
  15. package/build/cjs/events.js +4 -0
  16. package/build/cjs/events.js.map +1 -1
  17. package/build/cjs/index.js +3 -1
  18. package/build/cjs/index.js.map +1 -1
  19. package/build/cjs/useAudioRecorder.js +139 -4
  20. package/build/cjs/useAudioRecorder.js.map +1 -1
  21. package/build/esm/AudioRecorder.provider.js +3 -4
  22. package/build/esm/AudioRecorder.provider.js.map +1 -1
  23. package/build/esm/AudioStudio.types.js.map +1 -1
  24. package/build/esm/AudioStudio.web.js +125 -13
  25. package/build/esm/AudioStudio.web.js.map +1 -1
  26. package/build/esm/AudioStudioModule.js +6 -1
  27. package/build/esm/AudioStudioModule.js.map +1 -1
  28. package/build/esm/events.js +3 -0
  29. package/build/esm/events.js.map +1 -1
  30. package/build/esm/index.js +1 -0
  31. package/build/esm/index.js.map +1 -1
  32. package/build/esm/useAudioRecorder.js +140 -5
  33. package/build/esm/useAudioRecorder.js.map +1 -1
  34. package/build/types/AudioStudio.types.d.ts +44 -1
  35. package/build/types/AudioStudio.types.d.ts.map +1 -1
  36. package/build/types/AudioStudio.web.d.ts +17 -1
  37. package/build/types/AudioStudio.web.d.ts.map +1 -1
  38. package/build/types/AudioStudioModule.d.ts.map +1 -1
  39. package/build/types/events.d.ts +2 -1
  40. package/build/types/events.d.ts.map +1 -1
  41. package/build/types/index.d.ts +1 -0
  42. package/build/types/index.d.ts.map +1 -1
  43. package/build/types/useAudioRecorder.d.ts +2 -0
  44. package/build/types/useAudioRecorder.d.ts.map +1 -1
  45. package/ios/AudioStreamManager.swift +103 -9
  46. package/ios/AudioStreamManagerDelegate.swift +1 -0
  47. package/ios/AudioStudio.podspec +1 -1
  48. package/ios/AudioStudioModule.swift +6 -0
  49. package/ios/RecordingSettings.swift +48 -43
  50. package/package.json +163 -163
  51. package/plugin/tsconfig.json +8 -2
  52. package/src/AudioStudio.types.ts +48 -1
  53. package/src/AudioStudio.web.ts +152 -13
  54. package/src/AudioStudioModule.ts +6 -1
  55. package/src/events.ts +13 -1
  56. package/src/index.ts +1 -0
  57. package/src/useAudioRecorder.tsx +182 -2
  58. package/scripts/README.md +0 -58
@@ -4,7 +4,7 @@ import {
4
4
  AudioFeaturesOptions,
5
5
  DecodingConfig,
6
6
  } from './AudioAnalysis/AudioAnalysis.types'
7
- import { AudioAnalysisEvent } from './events'
7
+ import type { AudioAnalysisEvent } from './events'
8
8
 
9
9
  export interface CompressionInfo {
10
10
  /** Size of the compressed audio data in bytes */
@@ -36,6 +36,10 @@ export interface AudioStreamStatus {
36
36
  mimeType: string
37
37
  /** Information about audio compression if enabled */
38
38
  compression?: CompressionInfo
39
+ /** Configured maximum active recording duration in milliseconds, if enabled */
40
+ maxDurationMs?: number
41
+ /** Whether the current recording session has reached the configured maximum duration */
42
+ maxDurationReached?: boolean
39
43
  }
40
44
 
41
45
  interface AudioDataEventBase {
@@ -190,6 +194,19 @@ export interface StartRecordingResult {
190
194
  }
191
195
  }
192
196
 
197
+ export interface MaxDurationReachedEvent {
198
+ /** Active recording duration that triggered the event, in milliseconds */
199
+ durationMs: number
200
+ /** Configured active recording duration limit, in milliseconds */
201
+ maxDurationMs: number
202
+ /** Amount by which timer delivery exceeded the limit, in milliseconds */
203
+ overrunMs: number
204
+ /** Active stream identifier when available */
205
+ streamUuid?: string
206
+ /** Whether the recorder was configured to stop automatically after this event */
207
+ autoStopped: boolean
208
+ }
209
+
193
210
  export interface AudioSessionConfig {
194
211
  /**
195
212
  * Audio session category that defines the audio behavior
@@ -484,6 +501,30 @@ export interface RecordingConfig {
484
501
  /** Optional callback to handle recording interruptions */
485
502
  onRecordingInterrupted?: (_: RecordingInterruptionEvent) => void
486
503
 
504
+ /**
505
+ * Maximum cumulative active recording duration, in milliseconds.
506
+ *
507
+ * Paused time does not count. Set to undefined, 0, or a negative value to disable.
508
+ */
509
+ maxDurationMs?: number
510
+
511
+ /**
512
+ * Stop recording automatically when maxDurationMs is reached.
513
+ *
514
+ * Defaults to false. The MaxDurationReached event is emitted before the stop request.
515
+ * The automatic stop result is not returned to onMaxDurationReached; use the
516
+ * event and stream callbacks for immediate UI updates.
517
+ */
518
+ autoStopOnMaxDuration?: boolean
519
+
520
+ /**
521
+ * Optional callback invoked when maxDurationMs is reached.
522
+ *
523
+ * If autoStopOnMaxDuration is true, this callback is invoked before the
524
+ * recorder finishes stopping. The final stop result is not passed here.
525
+ */
526
+ onMaxDurationReached?: (_: MaxDurationReachedEvent) => void
527
+
487
528
  /** Optional directory path where output files will be saved */
488
529
  outputDirectory?: string // If not provided, uses default app directory
489
530
  /** Optional filename for the recording (uses UUID if not provided) */
@@ -710,10 +751,16 @@ export interface UseAudioRecorderState {
710
751
  size: number // Size in bytes of the recorded audio
711
752
  /** Information about compression if enabled */
712
753
  compression?: CompressionInfo
754
+ /** Configured maximum active recording duration in milliseconds, if enabled */
755
+ maxDurationMs?: number
756
+ /** Whether the current recording session has reached the configured maximum duration */
757
+ maxDurationReached?: boolean
713
758
  /** Analysis data for the recording if processing was enabled */
714
759
  analysisData?: AudioAnalysis // Analysis data for the recording depending on enableProcessing flag
715
760
  /** Optional callback to handle recording interruptions */
716
761
  onRecordingInterrupted?: (_: RecordingInterruptionEvent) => void
762
+ /** Optional callback invoked when maxDurationMs is reached */
763
+ onMaxDurationReached?: (_: MaxDurationReachedEvent) => void
717
764
  }
718
765
 
719
766
  /**
@@ -52,6 +52,7 @@ export interface AudioStudioWebProps {
52
52
  audioWorkletUrl: string
53
53
  featuresExtratorUrl: string
54
54
  maxBufferSize?: number // Maximum number of chunks to keep in memory
55
+ emitEvent?: (eventName: string, params: unknown) => void
55
56
  }
56
57
 
57
58
  export class AudioStudioWeb extends LegacyEventEmitter {
@@ -80,12 +81,19 @@ export class AudioStudioWeb extends LegacyEventEmitter {
80
81
  totalCompressedSize: number = 0
81
82
  private readonly maxBufferSize: number
82
83
  private eventCallback?: (event: AudioStreamEvent) => void
84
+ private readonly moduleEventEmitter?: (eventName: string, params: unknown) => void
85
+ private maxDurationTimer?: ReturnType<typeof setTimeout>
86
+ private maxDurationTargetMs = 0
87
+ private maxDurationAccumulatedActiveMs = 0
88
+ private maxDurationSegmentStartMs = 0
89
+ private maxDurationReached = false
83
90
 
84
91
  constructor({
85
92
  audioWorkletUrl,
86
93
  featuresExtratorUrl,
87
94
  logger,
88
95
  maxBufferSize = DEFAULT_MAX_BUFFER_SIZE,
96
+ emitEvent,
89
97
  }: AudioStudioWebProps) {
90
98
  const mockNativeModule = {
91
99
  addListener: () => {},
@@ -114,6 +122,125 @@ export class AudioStudioWeb extends LegacyEventEmitter {
114
122
  this.audioWorkletUrl = audioWorkletUrl
115
123
  this.featuresExtratorUrl = featuresExtratorUrl
116
124
  this.maxBufferSize = maxBufferSize
125
+ this.moduleEventEmitter = emitEvent
126
+ }
127
+
128
+ private emitModuleEvent(eventName: string, params: unknown) {
129
+ this.emit(eventName, params)
130
+ this.moduleEventEmitter?.(eventName, params)
131
+ }
132
+
133
+ private resetMaxDurationState(preserveReached = false) {
134
+ if (this.maxDurationTimer) {
135
+ clearTimeout(this.maxDurationTimer)
136
+ this.maxDurationTimer = undefined
137
+ }
138
+ this.maxDurationSegmentStartMs = 0
139
+ if (!preserveReached || !this.maxDurationReached) {
140
+ this.maxDurationTargetMs = 0
141
+ this.maxDurationAccumulatedActiveMs = 0
142
+ this.maxDurationReached = false
143
+ }
144
+ }
145
+
146
+ private getMaxDurationActiveMs(now = performance.now()) {
147
+ if (this.maxDurationSegmentStartMs <= 0) {
148
+ return this.maxDurationAccumulatedActiveMs
149
+ }
150
+ return (
151
+ this.maxDurationAccumulatedActiveMs +
152
+ (now - this.maxDurationSegmentStartMs)
153
+ )
154
+ }
155
+
156
+ private scheduleMaxDurationTimer() {
157
+ if (
158
+ this.maxDurationTargetMs <= 0 ||
159
+ this.maxDurationReached ||
160
+ !this.isRecording ||
161
+ this.isPaused
162
+ ) {
163
+ return
164
+ }
165
+
166
+ if (this.maxDurationTimer) {
167
+ clearTimeout(this.maxDurationTimer)
168
+ }
169
+
170
+ const remainingMs = Math.max(
171
+ 0,
172
+ this.maxDurationTargetMs - this.getMaxDurationActiveMs()
173
+ )
174
+ this.maxDurationTimer = setTimeout(() => {
175
+ this.maxDurationTimer = undefined
176
+ this.emitMaxDurationReached()
177
+ }, remainingMs)
178
+ }
179
+
180
+ private startMaxDurationTimer(recordingConfig: RecordingConfig) {
181
+ this.resetMaxDurationState()
182
+ const targetMs = Number(recordingConfig.maxDurationMs ?? 0)
183
+ if (!Number.isFinite(targetMs) || targetMs <= 0) {
184
+ return
185
+ }
186
+
187
+ this.maxDurationTargetMs = targetMs
188
+ this.maxDurationSegmentStartMs = performance.now()
189
+ this.scheduleMaxDurationTimer()
190
+ }
191
+
192
+ private pauseMaxDurationTimer() {
193
+ if (this.maxDurationTimer) {
194
+ clearTimeout(this.maxDurationTimer)
195
+ this.maxDurationTimer = undefined
196
+ }
197
+ if (this.maxDurationSegmentStartMs > 0) {
198
+ this.maxDurationAccumulatedActiveMs = this.getMaxDurationActiveMs()
199
+ this.maxDurationSegmentStartMs = 0
200
+ }
201
+ }
202
+
203
+ private resumeMaxDurationTimer() {
204
+ if (this.maxDurationTargetMs <= 0 || this.maxDurationReached) {
205
+ return
206
+ }
207
+ this.maxDurationSegmentStartMs = performance.now()
208
+ this.scheduleMaxDurationTimer()
209
+ }
210
+
211
+ private emitMaxDurationReached() {
212
+ if (this.maxDurationTargetMs <= 0 || this.maxDurationReached) {
213
+ return
214
+ }
215
+
216
+ const durationMs = Math.round(this.getMaxDurationActiveMs())
217
+ this.maxDurationReached = true
218
+ const autoStopped = !!this.recordingConfig?.autoStopOnMaxDuration
219
+ this.emitModuleEvent('MaxDurationReached', {
220
+ durationMs,
221
+ maxDurationMs: this.maxDurationTargetMs,
222
+ overrunMs: Math.max(0, durationMs - this.maxDurationTargetMs),
223
+ streamUuid: this.streamUuid ?? undefined,
224
+ autoStopped,
225
+ })
226
+ if (autoStopped) {
227
+ this.stopRecording().catch((error) => {
228
+ this.logger?.error(
229
+ 'Error auto-stopping on max duration:',
230
+ error
231
+ )
232
+ })
233
+ }
234
+ }
235
+
236
+ private flushExpiredMaxDuration() {
237
+ if (
238
+ this.maxDurationTargetMs > 0 &&
239
+ !this.maxDurationReached &&
240
+ this.getMaxDurationActiveMs() >= this.maxDurationTargetMs
241
+ ) {
242
+ this.emitMaxDurationReached()
243
+ }
117
244
  }
118
245
 
119
246
  // Utility to handle user media stream
@@ -275,6 +402,7 @@ export class AudioStudioWeb extends LegacyEventEmitter {
275
402
  this.currentInterval = recordingConfig.interval ?? 1000
276
403
  this.currentIntervalAnalysis = recordingConfig.intervalAnalysis ?? 500
277
404
  this.lastEmittedAnalysisTime = Date.now()
405
+ this.startMaxDurationTimer(recordingConfig)
278
406
 
279
407
  // Use custom filename if provided, otherwise fallback to timestamp
280
408
  if (recordingConfig.filename) {
@@ -321,11 +449,11 @@ export class AudioStudioWeb extends LegacyEventEmitter {
321
449
  // Update local state if the interruption should pause recording
322
450
  if (event.isPaused) {
323
451
  this.isPaused = true
452
+ this.pausedTime = Date.now()
453
+ this.pauseMaxDurationTimer()
324
454
 
325
455
  // If this is a device disconnection, handle according to behavior setting
326
456
  if (event.reason === 'deviceDisconnected') {
327
- this.pausedTime = Date.now()
328
-
329
457
  // Check if we should try fallback to another device
330
458
  if (
331
459
  this.recordingConfig?.deviceDisconnectionBehavior ===
@@ -339,7 +467,7 @@ export class AudioStudioWeb extends LegacyEventEmitter {
339
467
  this.handleDeviceFallback().catch((error) => {
340
468
  // If fallback fails, emit warning
341
469
  this.logger?.error('Device fallback failed:', error)
342
- this.emit('onRecordingInterrupted', {
470
+ this.emitModuleEvent('onRecordingInterrupted', {
343
471
  reason: 'deviceSwitchFailed',
344
472
  isPaused: true,
345
473
  timestamp: Date.now(),
@@ -352,15 +480,15 @@ export class AudioStudioWeb extends LegacyEventEmitter {
352
480
  this.logger?.warn(
353
481
  'Device disconnected - recording paused automatically'
354
482
  )
355
- this.emit('onRecordingInterrupted', event)
483
+ this.emitModuleEvent('onRecordingInterrupted', event)
356
484
  }
357
485
  } else {
358
486
  // For other interruption types, just emit the event
359
- this.emit('onRecordingInterrupted', event)
487
+ this.emitModuleEvent('onRecordingInterrupted', event)
360
488
  }
361
489
  } else {
362
490
  // If not causing a pause, just forward the event
363
- this.emit('onRecordingInterrupted', event)
491
+ this.emitModuleEvent('onRecordingInterrupted', event)
364
492
  }
365
493
  }
366
494
 
@@ -390,7 +518,7 @@ export class AudioStudioWeb extends LegacyEventEmitter {
390
518
  private customRecorderAnalysisCallback(
391
519
  audioAnalysisData: AudioAnalysis
392
520
  ): void {
393
- this.emit('AudioAnalysis', audioAnalysisData)
521
+ this.emitModuleEvent('AudioAnalysis', audioAnalysisData)
394
522
  }
395
523
 
396
524
  // Get recording duration
@@ -425,6 +553,7 @@ export class AudioStudioWeb extends LegacyEventEmitter {
425
553
  } else {
426
554
  this.currentDurationMs += chunkDurationMs
427
555
  }
556
+ this.flushExpiredMaxDuration()
428
557
 
429
558
  const audioEventPayload: AudioEventPayload = {
430
559
  fileUri,
@@ -445,7 +574,7 @@ export class AudioStudioWeb extends LegacyEventEmitter {
445
574
  : undefined,
446
575
  }
447
576
 
448
- this.emit('AudioData', audioEventPayload)
577
+ this.emitModuleEvent('AudioData', audioEventPayload)
449
578
  }
450
579
 
451
580
  // Stop recording
@@ -457,6 +586,7 @@ export class AudioStudioWeb extends LegacyEventEmitter {
457
586
  this.logger?.debug('Starting stop process')
458
587
 
459
588
  try {
589
+ this.pauseMaxDurationTimer()
460
590
  const { compressedBlob, uncompressedBlob } =
461
591
  await this.customRecorder.stop()
462
592
 
@@ -540,6 +670,7 @@ export class AudioStudioWeb extends LegacyEventEmitter {
540
670
  this.totalCompressedSize = 0
541
671
  this.lastEmittedCompressionSize = 0
542
672
  this.audioChunks = []
673
+ this.resetMaxDurationState(true)
543
674
 
544
675
  return result
545
676
  } catch (error) {
@@ -565,11 +696,13 @@ export class AudioStudioWeb extends LegacyEventEmitter {
565
696
  }
566
697
  this.isPaused = true
567
698
  this.pausedTime = Date.now()
699
+ this.pauseMaxDurationTimer()
568
700
  } catch (error) {
569
701
  this.logger?.error('Error in pauseRecording', error)
570
702
  // Even if the pause operation failed, make sure our state is consistent
571
703
  this.isPaused = true
572
704
  this.pausedTime = Date.now()
705
+ this.pauseMaxDurationTimer()
573
706
  }
574
707
  }
575
708
 
@@ -607,8 +740,9 @@ export class AudioStudioWeb extends LegacyEventEmitter {
607
740
  const pauseDuration = Date.now() - this.pausedTime
608
741
  this.recordingStartTime += pauseDuration
609
742
  this.pausedTime = 0
743
+ this.resumeMaxDurationTimer()
610
744
 
611
- this.emit('onRecordingInterrupted', {
745
+ this.emitModuleEvent('onRecordingInterrupted', {
612
746
  reason: 'userResumed',
613
747
  isPaused: false,
614
748
  timestamp: Date.now(),
@@ -616,7 +750,7 @@ export class AudioStudioWeb extends LegacyEventEmitter {
616
750
  } catch (error) {
617
751
  this.logger?.error('Resume failed:', error)
618
752
  // Fallback to emitting a general failure if resume fails unexpectedly
619
- this.emit('onRecordingInterrupted', {
753
+ this.emitModuleEvent('onRecordingInterrupted', {
620
754
  reason: 'resumeFailed', // Use a more specific reason
621
755
  isPaused: true, // Remain paused if resume fails
622
756
  timestamp: Date.now(),
@@ -628,6 +762,7 @@ export class AudioStudioWeb extends LegacyEventEmitter {
628
762
 
629
763
  // Get current status
630
764
  status() {
765
+ this.flushExpiredMaxDuration()
631
766
  const durationMs = this.getRecordingDuration()
632
767
 
633
768
  const status: AudioStreamStatus = {
@@ -651,6 +786,9 @@ export class AudioStudioWeb extends LegacyEventEmitter {
651
786
  compressedFileUri: `${this.streamUuid}.webm`,
652
787
  }
653
788
  : undefined,
789
+ maxDurationMs:
790
+ this.maxDurationTargetMs > 0 ? this.maxDurationTargetMs : undefined,
791
+ maxDurationReached: this.maxDurationReached,
654
792
  }
655
793
  return status
656
794
  }
@@ -708,7 +846,7 @@ export class AudioStudioWeb extends LegacyEventEmitter {
708
846
  // Try to get a fallback device
709
847
  const fallbackDeviceInfo = await this.getFallbackDevice()
710
848
  if (!fallbackDeviceInfo) {
711
- this.emit('onRecordingInterrupted', {
849
+ this.emitModuleEvent('onRecordingInterrupted', {
712
850
  reason: 'deviceSwitchFailed',
713
851
  isPaused: true,
714
852
  timestamp: Date.now(),
@@ -774,6 +912,7 @@ export class AudioStudioWeb extends LegacyEventEmitter {
774
912
  // Update recording state
775
913
  this.isPaused = false
776
914
  this.recordingStartTime = Date.now()
915
+ this.resumeMaxDurationTimer()
777
916
 
778
917
  // Restore size counters to maintain continuity
779
918
  this.currentSize = previousTotalSize
@@ -795,7 +934,7 @@ export class AudioStudioWeb extends LegacyEventEmitter {
795
934
  error
796
935
  )
797
936
  this.isPaused = true
798
- this.emit('onRecordingInterrupted', {
937
+ this.emitModuleEvent('onRecordingInterrupted', {
799
938
  reason: 'deviceSwitchFailed',
800
939
  isPaused: true,
801
940
  timestamp: Date.now(),
@@ -807,7 +946,7 @@ export class AudioStudioWeb extends LegacyEventEmitter {
807
946
  } catch (error) {
808
947
  this.logger?.error('Failed to use fallback device', error)
809
948
  this.isPaused = true
810
- this.emit('onRecordingInterrupted', {
949
+ this.emitModuleEvent('onRecordingInterrupted', {
811
950
  reason: 'deviceSwitchFailed',
812
951
  isPaused: true,
813
952
  timestamp: Date.now(),
@@ -10,7 +10,12 @@ if (Platform.OS === 'web') {
10
10
  let instance: AudioStudioWeb | null = null
11
11
 
12
12
  AudioStudioModule = (webProps: AudioStudioWebProps) => {
13
- instance ??= new AudioStudioWeb(webProps)
13
+ instance ??= new AudioStudioWeb({
14
+ ...webProps,
15
+ emitEvent: (eventName, params) => {
16
+ AudioStudioModule.sendEvent(eventName, params)
17
+ },
18
+ })
14
19
  return instance
15
20
  }
16
21
  AudioStudioModule.requestPermissionsAsync = async () => {
package/src/events.ts CHANGED
@@ -3,7 +3,10 @@
3
3
  import { LegacyEventEmitter, type EventSubscription } from 'expo-modules-core'
4
4
 
5
5
  import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types'
6
- import { RecordingInterruptionEvent } from './AudioStudio.types'
6
+ import type {
7
+ MaxDurationReachedEvent,
8
+ RecordingInterruptionEvent,
9
+ } from './AudioStudio.types'
7
10
  import AudioStudioModule from './AudioStudioModule'
8
11
 
9
12
  const emitter = new LegacyEventEmitter(AudioStudioModule)
@@ -61,3 +64,12 @@ export function addRecordingInterruptionListener(
61
64
 
62
65
  return subscription
63
66
  }
67
+
68
+ export function addMaxDurationReachedListener(
69
+ listener: (event: MaxDurationReachedEvent) => void
70
+ ): EventSubscription {
71
+ return emitter.addListener<MaxDurationReachedEvent>(
72
+ 'MaxDurationReached',
73
+ listener
74
+ )
75
+ }
package/src/index.ts CHANGED
@@ -25,6 +25,7 @@ import {
25
25
  } from './streamAudioData'
26
26
  import { trimAudio } from './trimAudio'
27
27
  import { useAudioRecorder } from './useAudioRecorder'
28
+ export { addMaxDurationReachedListener } from './events'
28
29
 
29
30
  export * from './utils/convertPCMToFloat32'
30
31
  export * from './utils/getWavFileInfo'