@siteed/expo-audio-stream 2.0.1 → 2.2.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/README.md +46 -27
- package/build/index.d.ts +11 -12
- package/build/index.js +44 -10
- package/package.json +49 -110
- package/src/index.ts +18 -33
- package/CHANGELOG.md +0 -195
- package/android/build.gradle +0 -105
- package/android/src/main/AndroidManifest.xml +0 -27
- package/android/src/main/java/net/siteed/audiostream/AudioAnalysisData.kt +0 -166
- package/android/src/main/java/net/siteed/audiostream/AudioDataEncoder.kt +0 -9
- package/android/src/main/java/net/siteed/audiostream/AudioFileHandler.kt +0 -131
- package/android/src/main/java/net/siteed/audiostream/AudioFormatUtils.kt +0 -103
- package/android/src/main/java/net/siteed/audiostream/AudioNotificationsManager.kt +0 -435
- package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +0 -1936
- package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +0 -1437
- package/android/src/main/java/net/siteed/audiostream/AudioRecordingService.kt +0 -138
- package/android/src/main/java/net/siteed/audiostream/Constants.kt +0 -20
- package/android/src/main/java/net/siteed/audiostream/EventSender.kt +0 -7
- package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +0 -509
- package/android/src/main/java/net/siteed/audiostream/FFT.kt +0 -99
- package/android/src/main/java/net/siteed/audiostream/Features.kt +0 -98
- package/android/src/main/java/net/siteed/audiostream/NotificationConfig.kt +0 -70
- package/android/src/main/java/net/siteed/audiostream/PermissionUtils.kt +0 -59
- package/android/src/main/java/net/siteed/audiostream/RecordingActionReceiver.kt +0 -59
- package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +0 -205
- package/android/src/main/java/net/siteed/audiostream/WaveformConfig.kt +0 -19
- package/android/src/main/java/net/siteed/audiostream/WaveformRenderer.kt +0 -159
- package/android/src/main/res/drawable/ic_default_action_icon.xml +0 -16
- package/android/src/main/res/drawable/ic_microphone.xml +0 -13
- package/android/src/main/res/drawable/ic_pause.xml +0 -10
- package/android/src/main/res/drawable/ic_play.xml +0 -10
- package/android/src/main/res/drawable/ic_stop.xml +0 -10
- package/android/src/main/res/layout/notification_recording.xml +0 -37
- package/android/src/main/test/java/net/siteed/audiostream/AudioProcessorTest.kt +0 -56
- package/app.plugin.js +0 -1
- package/build/AudioAnalysis/AudioAnalysis.types.d.ts +0 -144
- package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +0 -1
- package/build/AudioAnalysis/AudioAnalysis.types.js +0 -3
- package/build/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
- package/build/AudioAnalysis/extractAudioAnalysis.d.ts +0 -78
- package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +0 -1
- package/build/AudioAnalysis/extractAudioAnalysis.js +0 -229
- package/build/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
- package/build/AudioAnalysis/extractWaveform.d.ts +0 -8
- package/build/AudioAnalysis/extractWaveform.d.ts.map +0 -1
- package/build/AudioAnalysis/extractWaveform.js +0 -11
- package/build/AudioAnalysis/extractWaveform.js.map +0 -1
- package/build/AudioRecorder.provider.d.ts +0 -11
- package/build/AudioRecorder.provider.d.ts.map +0 -1
- package/build/AudioRecorder.provider.js +0 -37
- package/build/AudioRecorder.provider.js.map +0 -1
- package/build/ExpoAudioStream.native.d.ts +0 -3
- package/build/ExpoAudioStream.native.d.ts.map +0 -1
- package/build/ExpoAudioStream.native.js +0 -6
- package/build/ExpoAudioStream.native.js.map +0 -1
- package/build/ExpoAudioStream.types.d.ts +0 -206
- package/build/ExpoAudioStream.types.d.ts.map +0 -1
- package/build/ExpoAudioStream.types.js +0 -2
- package/build/ExpoAudioStream.types.js.map +0 -1
- package/build/ExpoAudioStream.web.d.ts +0 -59
- package/build/ExpoAudioStream.web.d.ts.map +0 -1
- package/build/ExpoAudioStream.web.js +0 -285
- package/build/ExpoAudioStream.web.js.map +0 -1
- package/build/ExpoAudioStreamModule.d.ts +0 -3
- package/build/ExpoAudioStreamModule.d.ts.map +0 -1
- package/build/ExpoAudioStreamModule.js +0 -239
- package/build/ExpoAudioStreamModule.js.map +0 -1
- package/build/WebRecorder.web.d.ts +0 -119
- package/build/WebRecorder.web.d.ts.map +0 -1
- package/build/WebRecorder.web.js +0 -436
- package/build/WebRecorder.web.js.map +0 -1
- package/build/constants.d.ts +0 -11
- package/build/constants.d.ts.map +0 -1
- package/build/constants.js +0 -14
- package/build/constants.js.map +0 -1
- package/build/events.d.ts +0 -26
- package/build/events.d.ts.map +0 -1
- package/build/events.js +0 -21
- package/build/events.js.map +0 -1
- package/build/index.d.ts.map +0 -1
- package/build/index.js.map +0 -1
- package/build/useAudioRecorder.d.ts +0 -21
- package/build/useAudioRecorder.d.ts.map +0 -1
- package/build/useAudioRecorder.js +0 -427
- package/build/useAudioRecorder.js.map +0 -1
- package/build/utils/BlobFix.d.ts +0 -9
- package/build/utils/BlobFix.d.ts.map +0 -1
- package/build/utils/BlobFix.js +0 -498
- package/build/utils/BlobFix.js.map +0 -1
- package/build/utils/audioProcessing.d.ts +0 -24
- package/build/utils/audioProcessing.d.ts.map +0 -1
- package/build/utils/audioProcessing.js +0 -133
- package/build/utils/audioProcessing.js.map +0 -1
- package/build/utils/concatenateBuffers.d.ts +0 -8
- package/build/utils/concatenateBuffers.d.ts.map +0 -1
- package/build/utils/concatenateBuffers.js +0 -21
- package/build/utils/concatenateBuffers.js.map +0 -1
- package/build/utils/convertPCMToFloat32.d.ts +0 -13
- package/build/utils/convertPCMToFloat32.d.ts.map +0 -1
- package/build/utils/convertPCMToFloat32.js +0 -120
- package/build/utils/convertPCMToFloat32.js.map +0 -1
- package/build/utils/encodingToBitDepth.d.ts +0 -5
- package/build/utils/encodingToBitDepth.d.ts.map +0 -1
- package/build/utils/encodingToBitDepth.js +0 -13
- package/build/utils/encodingToBitDepth.js.map +0 -1
- package/build/utils/getWavFileInfo.d.ts +0 -26
- package/build/utils/getWavFileInfo.d.ts.map +0 -1
- package/build/utils/getWavFileInfo.js +0 -92
- package/build/utils/getWavFileInfo.js.map +0 -1
- package/build/utils/writeWavHeader.d.ts +0 -49
- package/build/utils/writeWavHeader.d.ts.map +0 -1
- package/build/utils/writeWavHeader.js +0 -91
- package/build/utils/writeWavHeader.js.map +0 -1
- package/build/workers/InlineFeaturesExtractor.web.d.ts +0 -2
- package/build/workers/InlineFeaturesExtractor.web.d.ts.map +0 -1
- package/build/workers/InlineFeaturesExtractor.web.js +0 -828
- package/build/workers/InlineFeaturesExtractor.web.js.map +0 -1
- package/build/workers/inlineAudioWebWorker.web.d.ts +0 -2
- package/build/workers/inlineAudioWebWorker.web.d.ts.map +0 -1
- package/build/workers/inlineAudioWebWorker.web.js +0 -157
- package/build/workers/inlineAudioWebWorker.web.js.map +0 -1
- package/expo-module.config.json +0 -9
- package/ios/AudioAnalysisData.swift +0 -74
- package/ios/AudioNotificationManager.swift +0 -135
- package/ios/AudioProcessingHelpers.swift +0 -743
- package/ios/AudioProcessor.swift +0 -858
- package/ios/AudioStreamError.swift +0 -7
- package/ios/AudioStreamManager.swift +0 -1708
- package/ios/AudioStreamManagerDelegate.swift +0 -16
- package/ios/DataPoint.swift +0 -54
- package/ios/DecodingConfig.swift +0 -47
- package/ios/ExpoAudioStream.podspec +0 -27
- package/ios/ExpoAudioStreamModule.swift +0 -698
- package/ios/FFT.swift +0 -62
- package/ios/Features.swift +0 -95
- package/ios/Logger.swift +0 -7
- package/ios/NotificationExtension.swift +0 -15
- package/ios/RecordingResult.swift +0 -22
- package/ios/RecordingSettings.swift +0 -265
- package/ios/WaveformExtractor.swift +0 -105
- package/plugin/build/index.d.ts +0 -21
- package/plugin/build/index.js +0 -191
- package/plugin/src/index.ts +0 -278
- package/plugin/tsconfig.json +0 -10
- package/plugin/tsconfig.tsbuildinfo +0 -1
- package/src/AudioAnalysis/AudioAnalysis.types.ts +0 -165
- package/src/AudioAnalysis/extractAudioAnalysis.ts +0 -370
- package/src/AudioAnalysis/extractWaveform.ts +0 -22
- package/src/AudioRecorder.provider.tsx +0 -54
- package/src/ExpoAudioStream.native.ts +0 -6
- package/src/ExpoAudioStream.types.ts +0 -329
- package/src/ExpoAudioStream.web.ts +0 -359
- package/src/ExpoAudioStreamModule.ts +0 -286
- package/src/WebRecorder.web.ts +0 -580
- package/src/constants.ts +0 -18
- package/src/events.ts +0 -60
- package/src/useAudioRecorder.tsx +0 -620
- package/src/utils/BlobFix.ts +0 -559
- package/src/utils/audioProcessing.ts +0 -205
- package/src/utils/concatenateBuffers.ts +0 -24
- package/src/utils/convertPCMToFloat32.ts +0 -170
- package/src/utils/encodingToBitDepth.ts +0 -18
- package/src/utils/getWavFileInfo.ts +0 -132
- package/src/utils/writeWavHeader.ts +0 -114
- package/src/workers/InlineFeaturesExtractor.web.tsx +0 -827
- package/src/workers/inlineAudioWebWorker.web.tsx +0 -156
|
@@ -1,329 +0,0 @@
|
|
|
1
|
-
// packages/expo-audio-stream/src/ExpoAudioStream.types.ts
|
|
2
|
-
import {
|
|
3
|
-
AudioAnalysis,
|
|
4
|
-
AudioFeaturesOptions,
|
|
5
|
-
DecodingConfig,
|
|
6
|
-
} from './AudioAnalysis/AudioAnalysis.types'
|
|
7
|
-
import { AudioAnalysisEvent } from './events'
|
|
8
|
-
|
|
9
|
-
export interface CompressionInfo {
|
|
10
|
-
size: number
|
|
11
|
-
mimeType: string
|
|
12
|
-
bitrate: number
|
|
13
|
-
format: string
|
|
14
|
-
compressedFileUri?: string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface AudioStreamStatus {
|
|
18
|
-
isRecording: boolean
|
|
19
|
-
isPaused: boolean
|
|
20
|
-
durationMs: number
|
|
21
|
-
size: number
|
|
22
|
-
interval: number
|
|
23
|
-
intervalAnalysis: number
|
|
24
|
-
mimeType: string
|
|
25
|
-
compression?: CompressionInfo
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface AudioDataEvent {
|
|
29
|
-
data: string | Float32Array
|
|
30
|
-
position: number
|
|
31
|
-
fileUri: string
|
|
32
|
-
eventDataSize: number
|
|
33
|
-
totalSize: number
|
|
34
|
-
compression?: CompressionInfo & {
|
|
35
|
-
data?: string | Blob // Base64 (native) or Float32Array (web) encoded compressed data chunk
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export type EncodingType = 'pcm_32bit' | 'pcm_16bit' | 'pcm_8bit'
|
|
40
|
-
export type SampleRate = 16000 | 44100 | 48000
|
|
41
|
-
export type BitDepth = 8 | 16 | 32
|
|
42
|
-
export type PCMFormat = `pcm_${BitDepth}bit`
|
|
43
|
-
|
|
44
|
-
export type ConsoleLike = {
|
|
45
|
-
log: (message: string, ...args: unknown[]) => void
|
|
46
|
-
debug: (message: string, ...args: unknown[]) => void
|
|
47
|
-
info: (message: string, ...args: unknown[]) => void
|
|
48
|
-
warn: (message: string, ...args: unknown[]) => void
|
|
49
|
-
error: (message: string, ...args: unknown[]) => void
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface Chunk {
|
|
53
|
-
text: string
|
|
54
|
-
timestamp: [number, number | null]
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface TranscriberData {
|
|
58
|
-
id: string
|
|
59
|
-
isBusy: boolean
|
|
60
|
-
text: string
|
|
61
|
-
startTime: number
|
|
62
|
-
endTime: number
|
|
63
|
-
chunks: Chunk[]
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface AudioRecording {
|
|
67
|
-
fileUri: string
|
|
68
|
-
filename: string
|
|
69
|
-
durationMs: number
|
|
70
|
-
size: number
|
|
71
|
-
mimeType: string
|
|
72
|
-
channels: number
|
|
73
|
-
bitDepth: BitDepth
|
|
74
|
-
sampleRate: SampleRate
|
|
75
|
-
createdAt?: number
|
|
76
|
-
transcripts?: TranscriberData[]
|
|
77
|
-
analysisData?: AudioAnalysis // Analysis data for the recording depending on enableProcessing flag
|
|
78
|
-
compression?: CompressionInfo & {
|
|
79
|
-
compressedFileUri: string
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export interface StartRecordingResult {
|
|
84
|
-
fileUri: string
|
|
85
|
-
mimeType: string
|
|
86
|
-
channels?: number
|
|
87
|
-
bitDepth?: BitDepth
|
|
88
|
-
sampleRate?: SampleRate
|
|
89
|
-
compression?: CompressionInfo & {
|
|
90
|
-
compressedFileUri: string
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export interface AudioSessionConfig {
|
|
95
|
-
category?:
|
|
96
|
-
| 'Ambient'
|
|
97
|
-
| 'SoloAmbient'
|
|
98
|
-
| 'Playback'
|
|
99
|
-
| 'Record'
|
|
100
|
-
| 'PlayAndRecord'
|
|
101
|
-
| 'MultiRoute'
|
|
102
|
-
mode?:
|
|
103
|
-
| 'Default'
|
|
104
|
-
| 'VoiceChat'
|
|
105
|
-
| 'VideoChat'
|
|
106
|
-
| 'GameChat'
|
|
107
|
-
| 'VideoRecording'
|
|
108
|
-
| 'Measurement'
|
|
109
|
-
| 'MoviePlayback'
|
|
110
|
-
| 'SpokenAudio'
|
|
111
|
-
categoryOptions?: (
|
|
112
|
-
| 'MixWithOthers'
|
|
113
|
-
| 'DuckOthers'
|
|
114
|
-
| 'InterruptSpokenAudioAndMixWithOthers'
|
|
115
|
-
| 'AllowBluetooth'
|
|
116
|
-
| 'AllowBluetoothA2DP'
|
|
117
|
-
| 'AllowAirPlay'
|
|
118
|
-
| 'DefaultToSpeaker'
|
|
119
|
-
)[]
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export interface IOSConfig {
|
|
123
|
-
audioSession?: AudioSessionConfig
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Add new type for interruption reasons
|
|
127
|
-
export type RecordingInterruptionReason =
|
|
128
|
-
| 'audioFocusLoss'
|
|
129
|
-
| 'audioFocusGain'
|
|
130
|
-
| 'phoneCall'
|
|
131
|
-
| 'phoneCallEnded'
|
|
132
|
-
| 'recordingStopped'
|
|
133
|
-
|
|
134
|
-
// Add new interface for interruption events
|
|
135
|
-
export interface RecordingInterruptionEvent {
|
|
136
|
-
reason: RecordingInterruptionReason
|
|
137
|
-
isPaused: boolean
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export interface RecordingConfig {
|
|
141
|
-
// Sample rate for recording (16000, 44100, or 48000 Hz)
|
|
142
|
-
sampleRate?: SampleRate
|
|
143
|
-
|
|
144
|
-
// Number of audio channels (1 for mono, 2 for stereo)
|
|
145
|
-
channels?: 1 | 2
|
|
146
|
-
|
|
147
|
-
// Encoding type for the recording (pcm_32bit, pcm_16bit, pcm_8bit)
|
|
148
|
-
encoding?: EncodingType
|
|
149
|
-
|
|
150
|
-
// Interval in milliseconds at which to emit recording data
|
|
151
|
-
interval?: number
|
|
152
|
-
|
|
153
|
-
// Interval in milliseconds at which to emit analysis data
|
|
154
|
-
intervalAnalysis?: number
|
|
155
|
-
|
|
156
|
-
// Keep the device awake while recording (default is false)
|
|
157
|
-
keepAwake?: boolean
|
|
158
|
-
|
|
159
|
-
// Show a notification during recording (default is false)
|
|
160
|
-
showNotification?: boolean
|
|
161
|
-
|
|
162
|
-
// Show waveform in the notification (Android only, when showNotification is true)
|
|
163
|
-
showWaveformInNotification?: boolean
|
|
164
|
-
|
|
165
|
-
// Configuration for the notification
|
|
166
|
-
notification?: NotificationConfig
|
|
167
|
-
|
|
168
|
-
// Enable audio processing (default is false)
|
|
169
|
-
enableProcessing?: boolean
|
|
170
|
-
|
|
171
|
-
// iOS-specific configuration
|
|
172
|
-
ios?: IOSConfig
|
|
173
|
-
|
|
174
|
-
// Duration of each segment in milliseconds (default: 100)
|
|
175
|
-
segmentDurationMs?: number
|
|
176
|
-
|
|
177
|
-
// Feature options to extract (default is empty)
|
|
178
|
-
features?: AudioFeaturesOptions
|
|
179
|
-
|
|
180
|
-
// Callback function to handle audio stream
|
|
181
|
-
onAudioStream?: (_: AudioDataEvent) => Promise<void>
|
|
182
|
-
|
|
183
|
-
// Callback function to handle audio features extraction results
|
|
184
|
-
onAudioAnalysis?: (_: AudioAnalysisEvent) => Promise<void>
|
|
185
|
-
|
|
186
|
-
compression?: {
|
|
187
|
-
enabled: boolean
|
|
188
|
-
format: 'aac' | 'opus'
|
|
189
|
-
bitrate?: number
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Whether to automatically resume recording after an interruption (default is false)
|
|
193
|
-
autoResumeAfterInterruption?: boolean
|
|
194
|
-
|
|
195
|
-
// Optional callback to handle recording interruptions
|
|
196
|
-
onRecordingInterrupted?: (_: RecordingInterruptionEvent) => void
|
|
197
|
-
|
|
198
|
-
// Optional output configuration
|
|
199
|
-
outputDirectory?: string // If not provided, uses default app directory
|
|
200
|
-
filename?: string // If not provided, uses UUID
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
export interface NotificationConfig {
|
|
204
|
-
// Title of the notification
|
|
205
|
-
title?: string
|
|
206
|
-
|
|
207
|
-
// Main text content of the notification
|
|
208
|
-
text?: string
|
|
209
|
-
|
|
210
|
-
// Icon to be displayed in the notification (resource name or URI)
|
|
211
|
-
icon?: string
|
|
212
|
-
|
|
213
|
-
// Android-specific notification configuration
|
|
214
|
-
android?: {
|
|
215
|
-
// Unique identifier for the notification channel
|
|
216
|
-
channelId?: string
|
|
217
|
-
|
|
218
|
-
// User-visible name of the notification channel
|
|
219
|
-
channelName?: string
|
|
220
|
-
|
|
221
|
-
// User-visible description of the notification channel
|
|
222
|
-
channelDescription?: string
|
|
223
|
-
|
|
224
|
-
// Unique identifier for this notification
|
|
225
|
-
notificationId?: number
|
|
226
|
-
|
|
227
|
-
// List of actions that can be performed from the notification
|
|
228
|
-
actions?: NotificationAction[]
|
|
229
|
-
|
|
230
|
-
// Configuration for the waveform visualization in the notification
|
|
231
|
-
waveform?: WaveformConfig
|
|
232
|
-
|
|
233
|
-
// Color of the notification LED (if device supports it)
|
|
234
|
-
lightColor?: string
|
|
235
|
-
|
|
236
|
-
// Priority of the notification (affects how it's displayed)
|
|
237
|
-
priority?: 'min' | 'low' | 'default' | 'high' | 'max'
|
|
238
|
-
|
|
239
|
-
// Accent color for the notification (used for the app icon and buttons)
|
|
240
|
-
accentColor?: string
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// iOS-specific notification configuration
|
|
244
|
-
ios?: {
|
|
245
|
-
// Identifier for the notification category (used for grouping similar notifications)
|
|
246
|
-
categoryIdentifier?: string
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
export interface NotificationAction {
|
|
251
|
-
// Display title for the action
|
|
252
|
-
title: string
|
|
253
|
-
|
|
254
|
-
// Unique identifier for the action
|
|
255
|
-
identifier: string
|
|
256
|
-
|
|
257
|
-
// Icon to be displayed for the action (Android only)
|
|
258
|
-
icon?: string
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
export interface WaveformConfig {
|
|
262
|
-
color?: string // The color of the waveform (e.g., "#FFFFFF" for white)
|
|
263
|
-
opacity?: number // Opacity of the waveform (0.0 - 1.0)
|
|
264
|
-
strokeWidth?: number // Width of the waveform line (default: 1.5)
|
|
265
|
-
style?: 'stroke' | 'fill' // Drawing style: "stroke" for outline, "fill" for solid
|
|
266
|
-
mirror?: boolean // Whether to mirror the waveform (symmetrical display)
|
|
267
|
-
height?: number // Height of the waveform view in dp (default: 64)
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
export interface ExtractAudioDataOptions {
|
|
271
|
-
fileUri: string
|
|
272
|
-
// Time-based range (mutually exclusive with byte-based range)
|
|
273
|
-
startTimeMs?: number
|
|
274
|
-
endTimeMs?: number
|
|
275
|
-
// Byte-based range (mutually exclusive with time-based range)
|
|
276
|
-
position?: number
|
|
277
|
-
length?: number
|
|
278
|
-
/** Include normalized audio data in [-1, 1] range */
|
|
279
|
-
includeNormalizedData?: boolean
|
|
280
|
-
/** Include base64 encoded string representation of the audio data */
|
|
281
|
-
includeBase64Data?: boolean
|
|
282
|
-
/** Include WAV header in the PCM data (makes it a valid WAV file) */
|
|
283
|
-
includeWavHeader?: boolean
|
|
284
|
-
/** Logger for debugging - can pass console directly. */
|
|
285
|
-
logger?: ConsoleLike
|
|
286
|
-
/** Compute the checksum of the pcm data */
|
|
287
|
-
computeChecksum?: boolean
|
|
288
|
-
/** Target config for the normalized audio (Android and Web) */
|
|
289
|
-
decodingOptions?: DecodingConfig
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
export interface ExtractedAudioData {
|
|
293
|
-
/** Raw PCM audio data */
|
|
294
|
-
pcmData: Uint8Array
|
|
295
|
-
/** Normalized audio data in [-1, 1] range (when includeNormalizedData is true) */
|
|
296
|
-
normalizedData?: Float32Array
|
|
297
|
-
/** Base64 encoded string representation of the audio data (when includeBase64Data is true) */
|
|
298
|
-
base64Data?: string
|
|
299
|
-
/** Sample rate in Hz (e.g., 44100, 48000) */
|
|
300
|
-
sampleRate: number
|
|
301
|
-
/** Number of audio channels (1 for mono, 2 for stereo) */
|
|
302
|
-
channels: number
|
|
303
|
-
/** Bits per sample (8, 16, or 32) */
|
|
304
|
-
bitDepth: BitDepth
|
|
305
|
-
/** Duration of the audio in milliseconds */
|
|
306
|
-
durationMs: number
|
|
307
|
-
/** PCM format identifier (e.g., "pcm_16bit") */
|
|
308
|
-
format: PCMFormat
|
|
309
|
-
/** Total number of audio samples per channel */
|
|
310
|
-
samples: number
|
|
311
|
-
/** Whether the pcmData includes a WAV header */
|
|
312
|
-
hasWavHeader?: boolean
|
|
313
|
-
/** CRC32 Checksum of pcm data */
|
|
314
|
-
checksum?: number
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
export interface UseAudioRecorderState {
|
|
318
|
-
startRecording: (_: RecordingConfig) => Promise<StartRecordingResult>
|
|
319
|
-
stopRecording: () => Promise<AudioRecording | null>
|
|
320
|
-
pauseRecording: () => Promise<void>
|
|
321
|
-
resumeRecording: () => Promise<void>
|
|
322
|
-
isRecording: boolean
|
|
323
|
-
isPaused: boolean
|
|
324
|
-
durationMs: number // Duration of the recording
|
|
325
|
-
size: number // Size in bytes of the recorded audio
|
|
326
|
-
compression?: CompressionInfo
|
|
327
|
-
analysisData?: AudioAnalysis // Analysis data for the recording depending on enableProcessing flag
|
|
328
|
-
onRecordingInterrupted?: (_: RecordingInterruptionEvent) => void
|
|
329
|
-
}
|
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
// src/ExpoAudioStreamModule.web.ts
|
|
2
|
-
import { LegacyEventEmitter } from 'expo-modules-core'
|
|
3
|
-
|
|
4
|
-
import { AudioAnalysis } from './AudioAnalysis/AudioAnalysis.types'
|
|
5
|
-
import {
|
|
6
|
-
AudioRecording,
|
|
7
|
-
AudioStreamStatus,
|
|
8
|
-
BitDepth,
|
|
9
|
-
ConsoleLike,
|
|
10
|
-
RecordingConfig,
|
|
11
|
-
StartRecordingResult,
|
|
12
|
-
} from './ExpoAudioStream.types'
|
|
13
|
-
import { WebRecorder } from './WebRecorder.web'
|
|
14
|
-
import { AudioEventPayload } from './events'
|
|
15
|
-
import { encodingToBitDepth } from './utils/encodingToBitDepth'
|
|
16
|
-
|
|
17
|
-
export interface EmitAudioEventProps {
|
|
18
|
-
data: Float32Array
|
|
19
|
-
position: number
|
|
20
|
-
compression?: {
|
|
21
|
-
data: Blob
|
|
22
|
-
size: number
|
|
23
|
-
totalSize: number
|
|
24
|
-
mimeType: string
|
|
25
|
-
format: string
|
|
26
|
-
bitrate: number
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
export type EmitAudioEventFunction = (_: EmitAudioEventProps) => void
|
|
30
|
-
export type EmitAudioAnalysisFunction = (_: AudioAnalysis) => void
|
|
31
|
-
|
|
32
|
-
export interface ExpoAudioStreamWebProps {
|
|
33
|
-
logger?: ConsoleLike
|
|
34
|
-
audioWorkletUrl: string
|
|
35
|
-
featuresExtratorUrl: string
|
|
36
|
-
maxBufferSize?: number // Maximum number of chunks to keep in memory
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
40
|
-
customRecorder: WebRecorder | null
|
|
41
|
-
audioChunks: Float32Array[]
|
|
42
|
-
isRecording: boolean
|
|
43
|
-
isPaused: boolean
|
|
44
|
-
recordingStartTime: number
|
|
45
|
-
pausedTime: number
|
|
46
|
-
currentDurationMs: number
|
|
47
|
-
currentSize: number
|
|
48
|
-
currentInterval: number
|
|
49
|
-
currentIntervalAnalysis: number
|
|
50
|
-
lastEmittedSize: number
|
|
51
|
-
lastEmittedTime: number
|
|
52
|
-
lastEmittedCompressionSize: number
|
|
53
|
-
lastEmittedAnalysisTime: number
|
|
54
|
-
streamUuid: string | null
|
|
55
|
-
extension: 'webm' | 'wav' = 'wav' // Default extension is 'wav'
|
|
56
|
-
recordingConfig?: RecordingConfig
|
|
57
|
-
bitDepth: BitDepth // Bit depth of the audio
|
|
58
|
-
audioWorkletUrl: string
|
|
59
|
-
featuresExtratorUrl: string
|
|
60
|
-
logger?: ConsoleLike
|
|
61
|
-
latestPosition: number = 0
|
|
62
|
-
totalCompressedSize: number = 0
|
|
63
|
-
private readonly maxBufferSize: number
|
|
64
|
-
|
|
65
|
-
constructor({
|
|
66
|
-
audioWorkletUrl,
|
|
67
|
-
featuresExtratorUrl,
|
|
68
|
-
logger,
|
|
69
|
-
maxBufferSize = 100, // Default to storing last 100 chunks (1 chunk = 0.5 seconds)
|
|
70
|
-
}: ExpoAudioStreamWebProps) {
|
|
71
|
-
const mockNativeModule = {
|
|
72
|
-
addListener: () => {
|
|
73
|
-
// Not used on web
|
|
74
|
-
},
|
|
75
|
-
removeListeners: () => {
|
|
76
|
-
// Not used on web
|
|
77
|
-
},
|
|
78
|
-
}
|
|
79
|
-
super(mockNativeModule) // Pass the mock native module to the parent class
|
|
80
|
-
|
|
81
|
-
this.logger = logger
|
|
82
|
-
this.customRecorder = null
|
|
83
|
-
this.audioChunks = []
|
|
84
|
-
this.isRecording = false
|
|
85
|
-
this.isPaused = false
|
|
86
|
-
this.recordingStartTime = 0
|
|
87
|
-
this.pausedTime = 0
|
|
88
|
-
this.currentDurationMs = 0
|
|
89
|
-
this.currentSize = 0
|
|
90
|
-
this.bitDepth = 32 // Default
|
|
91
|
-
this.currentInterval = 1000 // Default interval in ms
|
|
92
|
-
this.currentIntervalAnalysis = 500 // Default analysis interval in ms
|
|
93
|
-
this.lastEmittedSize = 0
|
|
94
|
-
this.lastEmittedTime = 0
|
|
95
|
-
this.latestPosition = 0
|
|
96
|
-
this.lastEmittedCompressionSize = 0
|
|
97
|
-
this.lastEmittedAnalysisTime = 0
|
|
98
|
-
this.streamUuid = null // Initialize UUID on first recording start
|
|
99
|
-
this.audioWorkletUrl = audioWorkletUrl
|
|
100
|
-
this.featuresExtratorUrl = featuresExtratorUrl
|
|
101
|
-
this.maxBufferSize = maxBufferSize
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Utility to handle user media stream
|
|
105
|
-
async getMediaStream() {
|
|
106
|
-
try {
|
|
107
|
-
return await navigator.mediaDevices.getUserMedia({ audio: true })
|
|
108
|
-
} catch (error) {
|
|
109
|
-
this.logger?.error('Failed to get media stream:', error)
|
|
110
|
-
throw error
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Start recording with options
|
|
115
|
-
async startRecording(
|
|
116
|
-
recordingConfig: RecordingConfig = {}
|
|
117
|
-
): Promise<StartRecordingResult> {
|
|
118
|
-
if (this.isRecording) {
|
|
119
|
-
throw new Error('Recording is already in progress')
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
this.bitDepth = encodingToBitDepth({
|
|
123
|
-
encoding: recordingConfig.encoding ?? 'pcm_32bit',
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
const audioContext = new (window.AudioContext ||
|
|
127
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
128
|
-
// @ts-ignore - Allow webkitAudioContext for Safari
|
|
129
|
-
window.webkitAudioContext)()
|
|
130
|
-
const stream = await this.getMediaStream()
|
|
131
|
-
|
|
132
|
-
const source = audioContext.createMediaStreamSource(stream)
|
|
133
|
-
|
|
134
|
-
this.customRecorder = new WebRecorder({
|
|
135
|
-
logger: this.logger,
|
|
136
|
-
audioContext,
|
|
137
|
-
source,
|
|
138
|
-
recordingConfig,
|
|
139
|
-
emitAudioEventCallback: ({
|
|
140
|
-
data,
|
|
141
|
-
position,
|
|
142
|
-
compression,
|
|
143
|
-
}: EmitAudioEventProps) => {
|
|
144
|
-
// Keep only the latest chunks based on maxBufferSize
|
|
145
|
-
this.audioChunks.push(new Float32Array(data))
|
|
146
|
-
if (this.audioChunks.length > this.maxBufferSize) {
|
|
147
|
-
this.audioChunks.shift() // Remove oldest chunk
|
|
148
|
-
}
|
|
149
|
-
this.currentSize += data.byteLength
|
|
150
|
-
this.emitAudioEvent({ data, position, compression })
|
|
151
|
-
this.lastEmittedTime = Date.now()
|
|
152
|
-
this.lastEmittedSize = this.currentSize
|
|
153
|
-
this.lastEmittedCompressionSize = compression?.size ?? 0
|
|
154
|
-
},
|
|
155
|
-
emitAudioAnalysisCallback: (audioAnalysisData: AudioAnalysis) => {
|
|
156
|
-
this.logger?.log(`Emitted AudioAnalysis:`, audioAnalysisData)
|
|
157
|
-
this.emit('AudioAnalysis', audioAnalysisData)
|
|
158
|
-
},
|
|
159
|
-
})
|
|
160
|
-
await this.customRecorder.init()
|
|
161
|
-
this.customRecorder.start()
|
|
162
|
-
|
|
163
|
-
// // Set a timer to stop recording after 5 seconds
|
|
164
|
-
// setTimeout(() => {
|
|
165
|
-
// logger.log("AUTO Stopping recording");
|
|
166
|
-
// this.customRecorder?.stopAndPlay();
|
|
167
|
-
// this.isRecording = false;
|
|
168
|
-
// }, 3000);
|
|
169
|
-
|
|
170
|
-
this.isRecording = true
|
|
171
|
-
this.recordingConfig = recordingConfig
|
|
172
|
-
this.recordingStartTime = Date.now()
|
|
173
|
-
this.pausedTime = 0
|
|
174
|
-
this.isPaused = false
|
|
175
|
-
this.lastEmittedSize = 0
|
|
176
|
-
this.lastEmittedTime = 0
|
|
177
|
-
this.lastEmittedCompressionSize = 0
|
|
178
|
-
this.currentInterval = recordingConfig.interval ?? 1000
|
|
179
|
-
this.currentIntervalAnalysis = recordingConfig.intervalAnalysis ?? 500
|
|
180
|
-
this.lastEmittedAnalysisTime = Date.now()
|
|
181
|
-
|
|
182
|
-
// Use custom filename if provided, otherwise fallback to timestamp
|
|
183
|
-
if (recordingConfig.filename) {
|
|
184
|
-
// Remove any existing extension from the filename
|
|
185
|
-
this.streamUuid = recordingConfig.filename.replace(/\.[^/.]+$/, '')
|
|
186
|
-
} else {
|
|
187
|
-
this.streamUuid = Date.now().toString()
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const fileUri = `${this.streamUuid}.${this.extension}`
|
|
191
|
-
const streamConfig: StartRecordingResult = {
|
|
192
|
-
fileUri,
|
|
193
|
-
mimeType: `audio/${this.extension}`,
|
|
194
|
-
bitDepth: this.bitDepth,
|
|
195
|
-
channels: recordingConfig.channels ?? 1,
|
|
196
|
-
sampleRate: recordingConfig.sampleRate ?? 44100,
|
|
197
|
-
compression: recordingConfig.compression
|
|
198
|
-
? {
|
|
199
|
-
...recordingConfig.compression,
|
|
200
|
-
bitrate: recordingConfig.compression?.bitrate ?? 128000,
|
|
201
|
-
size: 0,
|
|
202
|
-
mimeType: 'audio/webm',
|
|
203
|
-
format: recordingConfig.compression?.format ?? 'opus',
|
|
204
|
-
compressedFileUri: '',
|
|
205
|
-
}
|
|
206
|
-
: undefined,
|
|
207
|
-
}
|
|
208
|
-
return streamConfig
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
emitAudioEvent({ data, position, compression }: EmitAudioEventProps) {
|
|
212
|
-
const fileUri = `${this.streamUuid}.${this.extension}`
|
|
213
|
-
if (compression?.size) {
|
|
214
|
-
this.lastEmittedCompressionSize = compression.size
|
|
215
|
-
this.totalCompressedSize = compression.totalSize
|
|
216
|
-
}
|
|
217
|
-
this.latestPosition = position
|
|
218
|
-
this.currentDurationMs = position * 1000 // Convert position (in seconds) to ms
|
|
219
|
-
|
|
220
|
-
const audioEventPayload: AudioEventPayload = {
|
|
221
|
-
fileUri,
|
|
222
|
-
mimeType: `audio/${this.extension}`,
|
|
223
|
-
lastEmittedSize: this.lastEmittedSize,
|
|
224
|
-
deltaSize: data.byteLength,
|
|
225
|
-
position,
|
|
226
|
-
totalSize: this.currentSize,
|
|
227
|
-
buffer: data,
|
|
228
|
-
streamUuid: this.streamUuid ?? '',
|
|
229
|
-
compression: compression
|
|
230
|
-
? {
|
|
231
|
-
data: compression?.data,
|
|
232
|
-
totalSize: this.totalCompressedSize,
|
|
233
|
-
eventDataSize: compression?.size ?? 0,
|
|
234
|
-
position,
|
|
235
|
-
}
|
|
236
|
-
: undefined,
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
this.emit('AudioData', audioEventPayload)
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Stop recording
|
|
243
|
-
async stopRecording(): Promise<AudioRecording> {
|
|
244
|
-
if (!this.customRecorder) {
|
|
245
|
-
throw new Error('Recorder is not initialized')
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
this.logger?.debug('[Stop] Starting stop process')
|
|
249
|
-
const startTime = performance.now()
|
|
250
|
-
|
|
251
|
-
try {
|
|
252
|
-
this.logger?.debug('[Stop] Stopping recorder')
|
|
253
|
-
const { compressedBlob } = await this.customRecorder.stop()
|
|
254
|
-
|
|
255
|
-
this.isRecording = false
|
|
256
|
-
this.isPaused = false
|
|
257
|
-
this.currentDurationMs = Date.now() - this.recordingStartTime
|
|
258
|
-
|
|
259
|
-
let compression: AudioRecording['compression']
|
|
260
|
-
let fileUri = `${this.streamUuid}.${this.extension}`
|
|
261
|
-
let mimeType = `audio/${this.extension}`
|
|
262
|
-
|
|
263
|
-
if (compressedBlob && this.recordingConfig?.compression?.enabled) {
|
|
264
|
-
const compressedUri = URL.createObjectURL(compressedBlob)
|
|
265
|
-
compression = {
|
|
266
|
-
compressedFileUri: compressedUri,
|
|
267
|
-
size: compressedBlob.size,
|
|
268
|
-
mimeType: 'audio/webm',
|
|
269
|
-
format: 'opus',
|
|
270
|
-
bitrate: this.recordingConfig.compression.bitrate ?? 128000,
|
|
271
|
-
}
|
|
272
|
-
// Use compressed values when compression is enabled
|
|
273
|
-
fileUri = compressedUri
|
|
274
|
-
mimeType = 'audio/webm'
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
this.logger?.debug(
|
|
278
|
-
`[Stop] Completed stop process in ${performance.now() - startTime}ms`,
|
|
279
|
-
{
|
|
280
|
-
durationMs: this.currentDurationMs,
|
|
281
|
-
compressedSize: compression?.size,
|
|
282
|
-
}
|
|
283
|
-
)
|
|
284
|
-
|
|
285
|
-
// Use the stored streamUuid (which contains our custom filename) for the final filename
|
|
286
|
-
const filename = `${this.streamUuid}.${this.extension}`
|
|
287
|
-
const result: AudioRecording = {
|
|
288
|
-
fileUri,
|
|
289
|
-
filename, // This will now use our custom filename
|
|
290
|
-
bitDepth: this.bitDepth,
|
|
291
|
-
createdAt: this.recordingStartTime,
|
|
292
|
-
channels: this.recordingConfig?.channels ?? 1,
|
|
293
|
-
sampleRate: this.recordingConfig?.sampleRate ?? 44100,
|
|
294
|
-
durationMs: this.currentDurationMs,
|
|
295
|
-
size: this.currentSize,
|
|
296
|
-
mimeType,
|
|
297
|
-
compression,
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Reset after creating the result
|
|
301
|
-
this.streamUuid = null
|
|
302
|
-
|
|
303
|
-
return result
|
|
304
|
-
} catch (error) {
|
|
305
|
-
this.logger?.error('[Stop] Error stopping recording:', error)
|
|
306
|
-
throw error
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Pause recording
|
|
311
|
-
async pauseRecording() {
|
|
312
|
-
if (!this.isRecording || this.isPaused) {
|
|
313
|
-
throw new Error('Recording is not active or already paused')
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (this.customRecorder) {
|
|
317
|
-
this.customRecorder.pause()
|
|
318
|
-
}
|
|
319
|
-
this.isPaused = true
|
|
320
|
-
this.pausedTime = Date.now()
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Resume recording
|
|
324
|
-
async resumeRecording() {
|
|
325
|
-
if (!this.isPaused) {
|
|
326
|
-
throw new Error('Recording is not paused')
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
if (this.customRecorder) {
|
|
330
|
-
this.customRecorder.resume()
|
|
331
|
-
}
|
|
332
|
-
this.isPaused = false
|
|
333
|
-
this.recordingStartTime += Date.now() - this.pausedTime
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Get current status
|
|
337
|
-
status() {
|
|
338
|
-
const status: AudioStreamStatus = {
|
|
339
|
-
isRecording: this.isRecording,
|
|
340
|
-
isPaused: this.isPaused,
|
|
341
|
-
durationMs: this.currentDurationMs,
|
|
342
|
-
size: this.currentSize,
|
|
343
|
-
interval: this.currentInterval,
|
|
344
|
-
intervalAnalysis: this.currentIntervalAnalysis,
|
|
345
|
-
mimeType: `audio/${this.extension}`,
|
|
346
|
-
compression: this.recordingConfig?.compression?.enabled
|
|
347
|
-
? {
|
|
348
|
-
size: this.totalCompressedSize,
|
|
349
|
-
mimeType: 'audio/webm',
|
|
350
|
-
format: this.recordingConfig.compression.format ?? 'opus',
|
|
351
|
-
bitrate:
|
|
352
|
-
this.recordingConfig.compression.bitrate ?? 128000,
|
|
353
|
-
compressedFileUri: `${this.streamUuid}.webm`,
|
|
354
|
-
}
|
|
355
|
-
: undefined,
|
|
356
|
-
}
|
|
357
|
-
return status
|
|
358
|
-
}
|
|
359
|
-
}
|