@siteed/expo-audio-stream 1.0.2 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/.size-limit.json +6 -0
  2. package/README.md +18 -176
  3. package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +1 -0
  4. package/app.plugin.js +1 -1
  5. package/build/AudioAnalysis/AudioAnalysis.types.d.ts +74 -0
  6. package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -0
  7. package/build/AudioAnalysis/AudioAnalysis.types.js +3 -0
  8. package/build/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
  9. package/build/AudioAnalysis/extractAudioAnalysis.d.ts +20 -0
  10. package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -0
  11. package/build/AudioAnalysis/extractAudioAnalysis.js +88 -0
  12. package/build/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
  13. package/build/AudioAnalysis/extractWaveform.d.ts +8 -0
  14. package/build/AudioAnalysis/extractWaveform.d.ts.map +1 -0
  15. package/build/AudioAnalysis/extractWaveform.js +14 -0
  16. package/build/AudioAnalysis/extractWaveform.js.map +1 -0
  17. package/build/AudioRecorder.provider.d.ts +15 -2
  18. package/build/AudioRecorder.provider.d.ts.map +1 -1
  19. package/build/AudioRecorder.provider.js +21 -8
  20. package/build/AudioRecorder.provider.js.map +1 -1
  21. package/build/ExpoAudioStream.native.d.ts.map +1 -1
  22. package/build/ExpoAudioStream.native.js +2 -2
  23. package/build/ExpoAudioStream.native.js.map +1 -1
  24. package/build/ExpoAudioStream.types.d.ts +33 -89
  25. package/build/ExpoAudioStream.types.d.ts.map +1 -1
  26. package/build/ExpoAudioStream.types.js.map +1 -1
  27. package/build/ExpoAudioStream.web.d.ts +10 -9
  28. package/build/ExpoAudioStream.web.d.ts.map +1 -1
  29. package/build/ExpoAudioStream.web.js +44 -25
  30. package/build/ExpoAudioStream.web.js.map +1 -1
  31. package/build/ExpoAudioStreamModule.d.ts.map +1 -1
  32. package/build/ExpoAudioStreamModule.js +13 -8
  33. package/build/ExpoAudioStreamModule.js.map +1 -1
  34. package/build/{WebRecorder.d.ts → WebRecorder.web.d.ts} +13 -9
  35. package/build/WebRecorder.web.d.ts.map +1 -0
  36. package/build/{WebRecorder.js → WebRecorder.web.js} +118 -63
  37. package/build/WebRecorder.web.js.map +1 -0
  38. package/build/constants.d.ts +11 -0
  39. package/build/constants.d.ts.map +1 -0
  40. package/build/constants.js +14 -0
  41. package/build/constants.js.map +1 -0
  42. package/build/events.d.ts +18 -0
  43. package/build/events.d.ts.map +1 -0
  44. package/build/events.js +15 -0
  45. package/build/events.js.map +1 -0
  46. package/build/index.d.ts +9 -17
  47. package/build/index.d.ts.map +1 -1
  48. package/build/index.js +7 -113
  49. package/build/index.js.map +1 -1
  50. package/build/logger.d.ts +9 -0
  51. package/build/logger.d.ts.map +1 -0
  52. package/build/logger.js +13 -0
  53. package/build/logger.js.map +1 -0
  54. package/build/useAudioRecorder.d.ts +20 -0
  55. package/build/useAudioRecorder.d.ts.map +1 -0
  56. package/build/{useAudioRecording.js → useAudioRecorder.js} +90 -86
  57. package/build/useAudioRecorder.js.map +1 -0
  58. package/build/utils/BlobFix.d.ts +9 -0
  59. package/build/utils/BlobFix.d.ts.map +1 -0
  60. package/build/utils/BlobFix.js +494 -0
  61. package/build/utils/BlobFix.js.map +1 -0
  62. package/build/utils/concatenateBuffers.d.ts +8 -0
  63. package/build/utils/concatenateBuffers.d.ts.map +1 -0
  64. package/build/utils/concatenateBuffers.js +21 -0
  65. package/build/utils/concatenateBuffers.js.map +1 -0
  66. package/build/utils/convertPCMToFloat32.d.ts +11 -0
  67. package/build/utils/convertPCMToFloat32.d.ts.map +1 -0
  68. package/build/utils/convertPCMToFloat32.js +54 -0
  69. package/build/utils/convertPCMToFloat32.js.map +1 -0
  70. package/build/utils/encodingToBitDepth.d.ts +5 -0
  71. package/build/utils/encodingToBitDepth.d.ts.map +1 -0
  72. package/build/utils/encodingToBitDepth.js +13 -0
  73. package/build/utils/encodingToBitDepth.js.map +1 -0
  74. package/build/utils/getWavFileInfo.d.ts +26 -0
  75. package/build/utils/getWavFileInfo.d.ts.map +1 -0
  76. package/build/utils/getWavFileInfo.js +92 -0
  77. package/build/utils/getWavFileInfo.js.map +1 -0
  78. package/build/utils/writeWavHeader.d.ts +9 -0
  79. package/build/utils/writeWavHeader.d.ts.map +1 -0
  80. package/build/utils/writeWavHeader.js +41 -0
  81. package/build/utils/writeWavHeader.js.map +1 -0
  82. package/build/workers/InlineFeaturesExtractor.web.d.ts +2 -0
  83. package/build/workers/InlineFeaturesExtractor.web.d.ts.map +1 -0
  84. package/build/workers/InlineFeaturesExtractor.web.js +303 -0
  85. package/build/workers/InlineFeaturesExtractor.web.js.map +1 -0
  86. package/build/workers/inlineAudioWebWorker.web.d.ts +2 -0
  87. package/build/workers/inlineAudioWebWorker.web.d.ts.map +1 -0
  88. package/build/workers/inlineAudioWebWorker.web.js +243 -0
  89. package/build/workers/inlineAudioWebWorker.web.js.map +1 -0
  90. package/expo-module.config.json +8 -17
  91. package/ios/AudioStreamManager.swift +40 -2
  92. package/ios/ExpoAudioStreamModule.swift +11 -0
  93. package/ios/RecordingResult.swift +1 -0
  94. package/package.json +72 -64
  95. package/plugin/build/index.d.ts +1 -1
  96. package/plugin/build/index.js +7 -7
  97. package/plugin/src/index.ts +47 -47
  98. package/plugin/tsconfig.json +8 -13
  99. package/publish.sh +0 -0
  100. package/src/AudioAnalysis/AudioAnalysis.types.ts +84 -0
  101. package/src/AudioAnalysis/extractAudioAnalysis.ts +147 -0
  102. package/src/AudioAnalysis/extractWaveform.ts +25 -0
  103. package/src/AudioRecorder.provider.tsx +59 -31
  104. package/src/ExpoAudioStream.native.ts +2 -2
  105. package/src/ExpoAudioStream.types.ts +58 -116
  106. package/src/ExpoAudioStream.web.ts +233 -205
  107. package/src/ExpoAudioStreamModule.ts +18 -12
  108. package/src/WebRecorder.web.ts +433 -0
  109. package/src/constants.ts +18 -0
  110. package/src/events.ts +39 -0
  111. package/src/index.ts +15 -176
  112. package/src/logger.ts +23 -0
  113. package/src/useAudioRecorder.tsx +420 -0
  114. package/src/utils/BlobFix.ts +550 -0
  115. package/src/utils/concatenateBuffers.ts +24 -0
  116. package/src/utils/convertPCMToFloat32.ts +75 -0
  117. package/src/utils/encodingToBitDepth.ts +18 -0
  118. package/src/utils/getWavFileInfo.ts +132 -0
  119. package/src/utils/writeWavHeader.ts +56 -0
  120. package/src/workers/InlineFeaturesExtractor.web.tsx +302 -0
  121. package/src/workers/inlineAudioWebWorker.web.tsx +242 -0
  122. package/tsconfig.json +12 -7
  123. package/build/WebRecorder.d.ts.map +0 -1
  124. package/build/WebRecorder.js.map +0 -1
  125. package/build/inlineAudioWebWorker.d.ts +0 -3
  126. package/build/inlineAudioWebWorker.d.ts.map +0 -1
  127. package/build/inlineAudioWebWorker.js +0 -340
  128. package/build/inlineAudioWebWorker.js.map +0 -1
  129. package/build/useAudioRecording.d.ts +0 -38
  130. package/build/useAudioRecording.d.ts.map +0 -1
  131. package/build/useAudioRecording.js.map +0 -1
  132. package/build/utils.d.ts +0 -31
  133. package/build/utils.d.ts.map +0 -1
  134. package/build/utils.js +0 -143
  135. package/build/utils.js.map +0 -1
  136. package/src/WebRecorder.ts +0 -364
  137. package/src/inlineAudioWebWorker.tsx +0 -340
  138. package/src/useAudioRecording.tsx +0 -410
  139. package/src/utils.ts +0 -189
@@ -1,135 +1,77 @@
1
- export interface AudioEventPayload {
2
- encoded?: string;
3
- buffer?: ArrayBuffer;
4
- fileUri: string;
5
- lastEmittedSize: number;
6
- position: number;
7
- deltaSize: number;
8
- totalSize: number;
9
- mimeType: string;
10
- streamUuid: string;
11
- }
12
-
13
- export interface AudioStreamResult {
14
- fileUri: string;
15
- webAudioUri?: string;
16
- durationMs: number;
17
- size: number;
18
- mimeType: string;
19
- channels?: number;
20
- bitDepth?: number;
21
- sampleRate?: number;
22
- }
23
-
24
- export interface StartAudioStreamResult {
25
- fileUri: string;
26
- mimeType: string;
27
- channels?: number;
28
- bitDepth?: number;
29
- sampleRate?: number;
30
- }
1
+ // packages/expo-audio-stream/src/ExpoAudioStream.types.ts
2
+ import {
3
+ AmplitudeAlgorithm,
4
+ AudioAnalysis,
5
+ AudioFeaturesOptions,
6
+ } from './AudioAnalysis/AudioAnalysis.types'
7
+ import { AudioAnalysisEvent } from './events'
31
8
 
32
9
  export interface AudioStreamStatus {
33
- isRecording: boolean;
34
- isPaused: boolean;
35
- durationMs: number;
36
- size: number;
37
- interval: number;
38
- mimeType: string;
10
+ isRecording: boolean
11
+ isPaused: boolean
12
+ durationMs: number
13
+ size: number
14
+ interval: number
15
+ mimeType: string
39
16
  }
40
17
 
41
18
  export interface AudioDataEvent {
42
- data: string | ArrayBuffer;
43
- position: number;
44
- fileUri: string;
45
- eventDataSize: number;
46
- totalSize: number;
19
+ data: string | Float32Array
20
+ position: number
21
+ fileUri: string
22
+ eventDataSize: number
23
+ totalSize: number
47
24
  }
48
25
 
49
- export interface AudioFeatures {
50
- energy: number;
51
- mfcc: number[];
52
- rms: number;
53
- minAmplitude: number;
54
- maxAmplitude: number;
55
- zcr: number;
56
- spectralCentroid: number;
57
- spectralFlatness: number;
58
- spectralRolloff: number;
59
- spectralBandwidth: number;
60
- chromagram: number[];
61
- tempo: number;
62
- hnr: number;
26
+ export type EncodingType = 'pcm_32bit' | 'pcm_16bit' | 'pcm_8bit'
27
+ export type SampleRate = 16000 | 44100 | 48000
28
+ export type BitDepth = 8 | 16 | 32
29
+
30
+ export interface Chunk {
31
+ text: string
32
+ timestamp: [number, number | null]
63
33
  }
64
34
 
65
- export interface AudioFeaturesOptions {
66
- energy?: boolean;
67
- mfcc?: boolean;
68
- rms?: boolean;
69
- zcr?: boolean;
70
- spectralCentroid?: boolean;
71
- spectralFlatness?: boolean;
72
- spectralRolloff?: boolean;
73
- spectralBandwidth?: boolean;
74
- chromagram?: boolean;
75
- tempo?: boolean;
76
- hnr?: boolean;
35
+ export interface TranscriberData {
36
+ isBusy: boolean
37
+ text: string
38
+ chunks: Chunk[]
77
39
  }
78
40
 
79
- export interface DataPoint {
80
- id: number;
81
- amplitude: number;
82
- activeSpeech?: boolean;
83
- dB?: number;
84
- silent?: boolean;
85
- features?: AudioFeatures;
86
- startTime?: number;
87
- endTime?: number;
88
- // start / end position in bytes
89
- startPosition?: number;
90
- endPosition?: number;
91
- // number of audio samples for this point (samples size depends on bit depth)
92
- samples?: number;
93
- // Id of the speaker for this point
94
- speaker?: number;
41
+ export interface AudioRecording {
42
+ fileUri: string
43
+ filename: string
44
+ durationMs: number
45
+ size: number
46
+ mimeType: string
47
+ channels: number
48
+ bitDepth: BitDepth
49
+ sampleRate: SampleRate
50
+ transcripts?: TranscriberData[]
51
+ wavPCMData?: Float32Array // Full PCM data for the recording in WAV format (only on web, for native use the fileUri)
52
+ analysisData?: AudioAnalysis // Analysis data for the recording depending on enableProcessing flag
95
53
  }
96
54
 
97
- export interface AudioAnalysisData {
98
- pointsPerSecond: number; // How many consolidated value per second
99
- durationMs: number; // Duration of the audio in milliseconds
100
- bitDepth: number; // Bit depth of the audio
101
- samples: number; // Size of the audio in bytes
102
- numberOfChannels: number; // Number of audio channels
103
- sampleRate: number; // Sample rate of the audio
104
- dataPoints: DataPoint[];
105
- amplitudeRange: {
106
- min: number;
107
- max: number;
108
- };
109
- speakerChanges?: {
110
- timestamp: number;
111
- speaker: number;
112
- }[];
55
+ export interface StartRecordingResult {
56
+ fileUri: string
57
+ mimeType: string
58
+ channels?: number
59
+ bitDepth?: BitDepth
60
+ sampleRate?: SampleRate
113
61
  }
114
62
 
115
- export type EncodingType = "pcm_32bit" | "pcm_16bit" | "pcm_8bit";
116
- export type SampleRate = 16000 | 44100 | 48000;
117
63
  export interface RecordingConfig {
118
- sampleRate?: SampleRate; // Sample rate for recording
119
- channels?: 1 | 2; // 1 or 2 (MONO or STEREO)
120
- encoding?: EncodingType; // Encoding type for the recording
121
- interval?: number; // Interval in milliseconds at which to emit recording data
122
-
123
- // Optional parameters for audio processing
124
- //TODO remove maxRecentDataDuration - should be replaced by maxDataPoints to 100.
125
- maxRecentDataDuration?: number; // Maximum duration of recent data to keep for processing (default is 10.0 seconds)
126
- enableProcessing?: boolean; // Boolean to enable/disable audio processing (default is false)
127
- pointsPerSecond?: number; // Number of data points to extract per second of audio (default is 1000)
128
- algorithm?: string; // Algorithm to use for extraction (default is "rms")
129
- features?: AudioFeaturesOptions; // Feature options to extract (default is empty)
64
+ sampleRate?: SampleRate // Sample rate for recording
65
+ channels?: 1 | 2 // 1 or 2 (MONO or STEREO)
66
+ encoding?: EncodingType // Encoding type for the recording
67
+ interval?: number // Interval in milliseconds at which to emit recording data
130
68
 
131
- // Optional paramters from web
69
+ // Optional parameters for audio processing
70
+ enableProcessing?: boolean // Boolean to enable/disable audio processing (default is false)
71
+ pointsPerSecond?: number // Number of data points to extract per second of audio (default is 1000)
72
+ algorithm?: AmplitudeAlgorithm // Algorithm to use for amplitude computation (default is "rms")
73
+ features?: AudioFeaturesOptions // Feature options to extract (default is empty)
132
74
 
133
- onAudioStream?: (_: AudioDataEvent) => Promise<void>; // Callback function to handle audio stream
134
- onProcessingResult?: (_: AudioAnalysisData) => Promise<void>; // Callback function to handle processing results
75
+ onAudioStream?: (_: AudioDataEvent) => Promise<void> // Callback function to handle audio stream
76
+ onAudioAnalysis?: (_: AudioAnalysisEvent) => Promise<void> // Callback function to handle audio features extraction results
135
77
  }
@@ -1,228 +1,256 @@
1
1
  // src/ExpoAudioStreamModule.web.ts
2
- import debug from "debug";
3
- import { EventEmitter } from "expo-modules-core";
2
+ import { EventEmitter } from 'expo-modules-core'
4
3
 
4
+ import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types'
5
5
  import {
6
- AudioAnalysisData,
7
- AudioEventPayload,
8
- AudioStreamResult,
9
- AudioStreamStatus,
10
- RecordingConfig,
11
- StartAudioStreamResult,
12
- } from "./ExpoAudioStream.types";
13
- import { WebRecorder } from "./WebRecorder";
14
- import { encodingToBitDepth } from "./utils";
6
+ AudioRecording,
7
+ AudioStreamStatus,
8
+ BitDepth,
9
+ RecordingConfig,
10
+ StartRecordingResult,
11
+ } from './ExpoAudioStream.types'
12
+ import { WebRecorder } from './WebRecorder.web'
13
+ import { AudioEventPayload } from './events'
14
+ import { getLogger } from './logger'
15
+ import { concatenateBuffers } from './utils/concatenateBuffers'
16
+ import { encodingToBitDepth } from './utils/encodingToBitDepth'
17
+ import { writeWavHeader } from './utils/writeWavHeader'
15
18
 
16
19
  export interface EmitAudioEventProps {
17
- data: ArrayBuffer;
18
- position: number;
20
+ data: Float32Array
21
+ position: number
19
22
  }
20
- export type EmitAudioEventFunction = (_: EmitAudioEventProps) => void;
21
- export type EmitAudioAnalysisFunction = (_: AudioAnalysisData) => void;
23
+ export type EmitAudioEventFunction = (_: EmitAudioEventProps) => void
24
+ export type EmitAudioAnalysisFunction = (_: AudioAnalysis) => void
22
25
 
23
26
  export interface ExpoAudioStreamWebProps {
24
- audioWorkletUrl: string;
25
- featuresExtratorUrl: string;
27
+ audioWorkletUrl: string
28
+ featuresExtratorUrl: string
26
29
  }
27
30
 
28
- // const log = debug("expo-audio-stream:useAudioRecording");
29
- const log = console;
31
+ const logger = getLogger('ExpoAudioStreamWeb')
32
+
30
33
  export class ExpoAudioStreamWeb extends EventEmitter {
31
- customRecorder: WebRecorder | null;
32
- audioChunks: ArrayBuffer[];
33
- isRecording: boolean;
34
- isPaused: boolean;
35
- recordingStartTime: number;
36
- pausedTime: number;
37
- currentDurationMs: number;
38
- currentSize: number;
39
- currentInterval: number;
40
- lastEmittedSize: number;
41
- lastEmittedTime: number;
42
- streamUuid: string | null;
43
- extension: "webm" | "wav" = "wav"; // Default extension is 'webm'
44
- recordingConfig?: RecordingConfig;
45
- bitDepth: number; // Bit depth of the audio
46
- audioWorkletUrl: string;
47
- featuresExtratorUrl: string;
48
-
49
- constructor({
50
- audioWorkletUrl,
51
- featuresExtratorUrl,
52
- }: ExpoAudioStreamWebProps) {
53
- const mockNativeModule = {
54
- addListener: (eventName: string) => {
55
- // Not used on web
56
- },
57
- removeListeners: (count: number) => {
58
- // Not used on web
59
- },
60
- };
61
- super(mockNativeModule); // Pass the mock native module to the parent class
62
-
63
- this.customRecorder = null;
64
- this.audioChunks = [];
65
- this.isRecording = false;
66
- this.isPaused = false;
67
- this.recordingStartTime = 0;
68
- this.pausedTime = 0;
69
- this.currentDurationMs = 0;
70
- this.currentSize = 0;
71
- this.bitDepth = 32; // Default
72
- this.currentInterval = 1000; // Default interval in ms
73
- this.lastEmittedSize = 0;
74
- this.lastEmittedTime = 0;
75
- this.streamUuid = null; // Initialize UUID on first recording start
76
- this.audioWorkletUrl = audioWorkletUrl;
77
- this.featuresExtratorUrl = featuresExtratorUrl;
78
- }
79
-
80
- // Utility to handle user media stream
81
- async getMediaStream() {
82
- try {
83
- return await navigator.mediaDevices.getUserMedia({ audio: true });
84
- } catch (error) {
85
- console.error("Failed to get media stream:", error);
86
- throw error;
34
+ customRecorder: WebRecorder | null
35
+ audioChunks: ArrayBuffer[]
36
+ isRecording: boolean
37
+ isPaused: boolean
38
+ recordingStartTime: number
39
+ pausedTime: number
40
+ currentDurationMs: number
41
+ currentSize: number
42
+ currentInterval: number
43
+ lastEmittedSize: number
44
+ lastEmittedTime: number
45
+ streamUuid: string | null
46
+ extension: 'webm' | 'wav' = 'wav' // Default extension is 'webm'
47
+ recordingConfig?: RecordingConfig
48
+ bitDepth: BitDepth // Bit depth of the audio
49
+ audioWorkletUrl: string
50
+ featuresExtratorUrl: string
51
+
52
+ constructor({
53
+ audioWorkletUrl,
54
+ featuresExtratorUrl,
55
+ }: ExpoAudioStreamWebProps) {
56
+ const mockNativeModule = {
57
+ addListener: () => {
58
+ // Not used on web
59
+ },
60
+ removeListeners: () => {
61
+ // Not used on web
62
+ },
63
+ }
64
+ super(mockNativeModule) // Pass the mock native module to the parent class
65
+
66
+ this.customRecorder = null
67
+ this.audioChunks = []
68
+ this.isRecording = false
69
+ this.isPaused = false
70
+ this.recordingStartTime = 0
71
+ this.pausedTime = 0
72
+ this.currentDurationMs = 0
73
+ this.currentSize = 0
74
+ this.bitDepth = 32 // Default
75
+ this.currentInterval = 1000 // Default interval in ms
76
+ this.lastEmittedSize = 0
77
+ this.lastEmittedTime = 0
78
+ this.streamUuid = null // Initialize UUID on first recording start
79
+ this.audioWorkletUrl = audioWorkletUrl
80
+ this.featuresExtratorUrl = featuresExtratorUrl
87
81
  }
88
- }
89
82
 
90
- // Start recording with options
91
- async startRecording(recordingConfig: RecordingConfig = {}) {
92
- if (this.isRecording) {
93
- throw new Error("Recording is already in progress");
83
+ // Utility to handle user media stream
84
+ async getMediaStream() {
85
+ try {
86
+ return await navigator.mediaDevices.getUserMedia({ audio: true })
87
+ } catch (error) {
88
+ console.error('Failed to get media stream:', error)
89
+ throw error
90
+ }
91
+ }
92
+
93
+ // Start recording with options
94
+ async startRecording(recordingConfig: RecordingConfig = {}) {
95
+ if (this.isRecording) {
96
+ throw new Error('Recording is already in progress')
97
+ }
98
+
99
+ this.bitDepth = encodingToBitDepth({
100
+ encoding: recordingConfig.encoding ?? 'pcm_32bit',
101
+ })
102
+
103
+ const audioContext = new (window.AudioContext ||
104
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
105
+ // @ts-ignore - Allow webkitAudioContext for Safari
106
+ window.webkitAudioContext)()
107
+ const stream = await this.getMediaStream()
108
+
109
+ const source = audioContext.createMediaStreamSource(stream)
110
+
111
+ this.customRecorder = new WebRecorder({
112
+ audioContext,
113
+ source,
114
+ recordingConfig,
115
+ audioWorkletUrl: this.audioWorkletUrl,
116
+ emitAudioEventCallback: ({
117
+ data,
118
+ position,
119
+ }: EmitAudioEventProps) => {
120
+ this.audioChunks.push(data)
121
+ this.currentSize += data.byteLength
122
+ this.emitAudioEvent({ data, position })
123
+ this.lastEmittedTime = Date.now()
124
+ this.lastEmittedSize = this.currentSize
125
+ },
126
+ emitAudioAnalysisCallback: (audioAnalysisData: AudioAnalysis) => {
127
+ logger.log(`Emitted AudioAnalysis:`, audioAnalysisData)
128
+ this.emit('AudioAnalysis', audioAnalysisData)
129
+ },
130
+ })
131
+ await this.customRecorder.init()
132
+ this.customRecorder.start()
133
+
134
+ // // Set a timer to stop recording after 5 seconds
135
+ // setTimeout(() => {
136
+ // logger.log("AUTO Stopping recording");
137
+ // this.customRecorder?.stopAndPlay();
138
+ // this.isRecording = false;
139
+ // }, 3000);
140
+
141
+ this.isRecording = true
142
+ this.recordingConfig = recordingConfig
143
+ this.recordingStartTime = Date.now()
144
+ this.pausedTime = 0
145
+ this.lastEmittedSize = 0
146
+ this.lastEmittedTime = 0
147
+ this.streamUuid = Date.now().toString()
148
+ const fileUri = `${this.streamUuid}.${this.extension}`
149
+ const streamConfig: StartRecordingResult = {
150
+ fileUri,
151
+ mimeType: `audio/${this.extension}`,
152
+ bitDepth: this.bitDepth,
153
+ channels: recordingConfig.channels ?? 1,
154
+ sampleRate: recordingConfig.sampleRate ?? 44100,
155
+ }
156
+ return streamConfig
94
157
  }
95
158
 
96
- this.bitDepth = encodingToBitDepth({
97
- encoding: recordingConfig.encoding ?? "pcm_32bit",
98
- });
99
-
100
- const audioContext = new (window.AudioContext ||
101
- // @ts-ignore - Allow webkitAudioContext for Safari
102
- window.webkitAudioContext)();
103
- const stream = await this.getMediaStream();
104
-
105
- const source = audioContext.createMediaStreamSource(stream);
106
-
107
- this.customRecorder = new WebRecorder({
108
- audioContext,
109
- source,
110
- recordingConfig,
111
- audioWorkletUrl: this.audioWorkletUrl,
112
- featuresExtratorUrl: this.featuresExtratorUrl,
113
- emitAudioEventCallback: ({ data, position }: EmitAudioEventProps) => {
114
- this.audioChunks.push(data);
115
- this.currentSize += data.byteLength;
116
- this.emitAudioEvent({ data, position });
117
- this.lastEmittedTime = Date.now();
118
- this.lastEmittedSize = this.currentSize;
119
- },
120
- emitAudioAnalysisCallback: (audioAnalysisData: AudioAnalysisData) => {
121
- console.log(`Emitted AudioAnalysis:`, audioAnalysisData);
122
- this.emit("AudioAnalysis", audioAnalysisData);
123
- },
124
- });
125
- await this.customRecorder.init();
126
- this.customRecorder.start();
127
-
128
- // // Set a timer to stop recording after 5 seconds
129
- // setTimeout(() => {
130
- // console.log("AUTO Stopping recording");
131
- // this.customRecorder?.stopAndPlay();
132
- // this.isRecording = false;
133
- // }, 3000);
134
-
135
- this.isRecording = true;
136
- this.recordingConfig = recordingConfig;
137
- this.recordingStartTime = Date.now();
138
- this.pausedTime = 0;
139
- this.lastEmittedSize = 0;
140
- this.lastEmittedTime = 0;
141
- this.streamUuid = Date.now().toString();
142
- const fileUri = `${this.streamUuid}.${this.extension}`;
143
- const streamConfig: StartAudioStreamResult = {
144
- fileUri,
145
- mimeType: `audio/${this.extension}`,
146
- bitDepth: this.bitDepth,
147
- channels: recordingConfig.channels ?? 1,
148
- sampleRate: recordingConfig.sampleRate ?? 44100,
149
- };
150
- return streamConfig;
151
- }
152
-
153
- emitAudioEvent({ data, position }: EmitAudioEventProps) {
154
- const fileUri = `${this.streamUuid}.${this.extension}`;
155
- const audioEventPayload: AudioEventPayload = {
156
- fileUri,
157
- mimeType: `audio/${this.extension}`,
158
- lastEmittedSize: this.lastEmittedSize, // Since this might be continuously streaming, adjust accordingly
159
- deltaSize: data.byteLength,
160
- position,
161
- totalSize: this.currentSize,
162
- buffer: data,
163
- streamUuid: this.streamUuid ?? "", // Generate or manage UUID for stream identification
164
- };
165
-
166
- this.emit("AudioData", audioEventPayload);
167
- }
168
-
169
- // Stop recording
170
- async stopRecording(): Promise<AudioStreamResult | null> {
171
- if (this.customRecorder) {
172
- const fullPcmBuffer = await this.customRecorder.stop();
173
- log.debug(`Stopped recording`, fullPcmBuffer);
159
+ emitAudioEvent({ data, position }: EmitAudioEventProps) {
160
+ const fileUri = `${this.streamUuid}.${this.extension}`
161
+ const audioEventPayload: AudioEventPayload = {
162
+ fileUri,
163
+ mimeType: `audio/${this.extension}`,
164
+ lastEmittedSize: this.lastEmittedSize, // Since this might be continuously streaming, adjust accordingly
165
+ deltaSize: data.byteLength,
166
+ position,
167
+ totalSize: this.currentSize,
168
+ buffer: data,
169
+ streamUuid: this.streamUuid ?? '', // Generate or manage UUID for stream identification
170
+ }
171
+
172
+ this.emit('AudioData', audioEventPayload)
174
173
  }
175
- this.isRecording = false;
176
- this.currentDurationMs = Date.now() - this.recordingStartTime;
177
- const result: AudioStreamResult = {
178
- fileUri: `${this.streamUuid}.${this.extension}`,
179
- bitDepth: this.bitDepth,
180
- channels: this.recordingConfig?.channels ?? 1,
181
- sampleRate: this.recordingConfig?.sampleRate ?? 44100,
182
- durationMs: this.currentDurationMs,
183
- size: this.currentSize,
184
- mimeType: `audio/${this.extension}`,
185
- };
186
-
187
- return result;
188
- }
189
-
190
- // Pause recording
191
- async pauseRecording() {
192
- if (!this.isRecording || this.isPaused) {
193
- throw new Error("Recording is not active or already paused");
174
+
175
+ // Stop recording
176
+ async stopRecording(): Promise<AudioRecording> {
177
+ if (!this.customRecorder) {
178
+ throw new Error('Recorder is not initialized')
179
+ }
180
+
181
+ const fullPcmBufferArray = await this.customRecorder.stop()
182
+
183
+ // concat all audio chunks
184
+ logger.debug(`Stopped recording`, fullPcmBufferArray)
185
+ this.isRecording = false
186
+ this.currentDurationMs = Date.now() - this.recordingStartTime
187
+
188
+ const wavConfig = {
189
+ buffer: fullPcmBufferArray.buffer,
190
+ sampleRate: this.recordingConfig?.sampleRate ?? 44100,
191
+ numChannels: this.recordingConfig?.channels ?? 1,
192
+ bitDepth: this.bitDepth,
193
+ }
194
+ logger.debug(`Writing wav header`, wavConfig)
195
+ const wavBuffer = writeWavHeader(wavConfig).slice(0)
196
+
197
+ // Create blob fileUri from audio chunks
198
+ const blob = new Blob([wavBuffer], {
199
+ type: `audio/${this.extension}`,
200
+ })
201
+ const fileUri = URL.createObjectURL(blob)
202
+
203
+ const result: AudioRecording = {
204
+ fileUri,
205
+ filename: `${this.streamUuid}.${this.extension}`,
206
+ wavPCMData: fullPcmBufferArray,
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: `audio/${this.extension}`,
213
+ }
214
+
215
+ return result
194
216
  }
195
217
 
196
- if (this.customRecorder) {
197
- this.customRecorder.stop();
218
+ // Pause recording
219
+ async pauseRecording() {
220
+ if (!this.isRecording || this.isPaused) {
221
+ throw new Error('Recording is not active or already paused')
222
+ }
223
+
224
+ if (this.customRecorder) {
225
+ this.customRecorder.pause()
226
+ }
227
+ this.isPaused = true
228
+ this.pausedTime = Date.now()
198
229
  }
199
- this.isPaused = true;
200
- this.pausedTime = Date.now();
201
- }
202
-
203
- // Resume recording
204
- async resumeRecording() {
205
- if (!this.isPaused) {
206
- throw new Error("Recording is not paused");
230
+
231
+ // Resume recording
232
+ async resumeRecording() {
233
+ if (!this.isPaused) {
234
+ throw new Error('Recording is not paused')
235
+ }
236
+
237
+ if (this.customRecorder) {
238
+ this.customRecorder.resume()
239
+ }
240
+ this.isPaused = false
241
+ this.recordingStartTime += Date.now() - this.pausedTime
207
242
  }
208
243
 
209
- if (this.customRecorder) {
210
- this.customRecorder.resume();
244
+ // Get current status
245
+ status() {
246
+ const status: AudioStreamStatus = {
247
+ isRecording: this.isRecording,
248
+ isPaused: this.isPaused,
249
+ durationMs: Date.now() - this.recordingStartTime,
250
+ size: this.currentSize,
251
+ interval: this.currentInterval,
252
+ mimeType: `audio/${this.extension}`,
253
+ }
254
+ return status
211
255
  }
212
- this.isPaused = false;
213
- this.recordingStartTime += Date.now() - this.pausedTime;
214
- }
215
-
216
- // Get current status
217
- status() {
218
- const status: AudioStreamStatus = {
219
- isRecording: this.isRecording,
220
- isPaused: this.isPaused,
221
- durationMs: Date.now() - this.recordingStartTime,
222
- size: this.currentSize,
223
- interval: this.currentInterval,
224
- mimeType: `audio/${this.extension}`,
225
- };
226
- return status;
227
- }
228
256
  }