@siteed/expo-audio-stream 1.15.1 → 1.17.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 +14 -1
- package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +136 -23
- package/android/src/main/java/net/siteed/audiostream/Constants.kt +1 -0
- package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +2 -0
- package/build/ExpoAudioStream.types.d.ts +2 -0
- package/build/ExpoAudioStream.types.d.ts.map +1 -1
- package/build/ExpoAudioStream.types.js.map +1 -1
- package/build/ExpoAudioStream.web.d.ts +2 -0
- package/build/ExpoAudioStream.web.d.ts.map +1 -1
- package/build/ExpoAudioStream.web.js +8 -0
- package/build/ExpoAudioStream.web.js.map +1 -1
- package/build/WebRecorder.web.d.ts.map +1 -1
- package/build/WebRecorder.web.js +1 -0
- package/build/WebRecorder.web.js.map +1 -1
- package/build/workers/InlineFeaturesExtractor.web.d.ts +1 -1
- package/build/workers/InlineFeaturesExtractor.web.d.ts.map +1 -1
- package/build/workers/InlineFeaturesExtractor.web.js +21 -4
- package/build/workers/InlineFeaturesExtractor.web.js.map +1 -1
- package/ios/AudioStreamManager.swift +81 -29
- package/ios/ExpoAudioStreamModule.swift +2 -1
- package/ios/RecordingSettings.swift +2 -0
- package/package.json +1 -1
- package/plugin/build/index.d.ts +2 -0
- package/plugin/build/index.js +21 -13
- package/plugin/src/index.ts +26 -14
- package/src/ExpoAudioStream.types.ts +6 -2
- package/src/ExpoAudioStream.web.ts +8 -0
- package/src/WebRecorder.web.ts +1 -0
- package/src/workers/InlineFeaturesExtractor.web.tsx +21 -4
package/package.json
CHANGED
package/plugin/build/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ interface AudioStreamPluginOptions {
|
|
|
13
13
|
iosConfig?: {
|
|
14
14
|
allowBackgroundAudioControls?: boolean;
|
|
15
15
|
backgroundProcessingTitle?: string;
|
|
16
|
+
microphoneUsageDescription?: string;
|
|
17
|
+
notificationUsageDescription?: string;
|
|
16
18
|
};
|
|
17
19
|
}
|
|
18
20
|
declare const withRecordingPermission: ConfigPlugin<AudioStreamPluginOptions>;
|
package/plugin/build/index.js
CHANGED
|
@@ -21,29 +21,37 @@ const withRecordingPermission = (config, props) => {
|
|
|
21
21
|
useLocation: false,
|
|
22
22
|
useExternalAccessory: false,
|
|
23
23
|
},
|
|
24
|
+
iosConfig: {
|
|
25
|
+
microphoneUsageDescription: MICROPHONE_USAGE,
|
|
26
|
+
notificationUsageDescription: NOTIFICATION_USAGE,
|
|
27
|
+
},
|
|
24
28
|
...(props || {}),
|
|
25
29
|
};
|
|
26
30
|
const { enablePhoneStateHandling, enableNotifications, enableBackgroundAudio, } = options;
|
|
27
31
|
debugLog('📱 Configuring Recording Permissions Plugin...', options);
|
|
28
32
|
// iOS Configuration
|
|
29
33
|
config = (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
30
|
-
//
|
|
34
|
+
// Always set the microphone usage description from options first
|
|
31
35
|
config.modResults['NSMicrophoneUsageDescription'] =
|
|
32
|
-
|
|
36
|
+
options.iosConfig?.microphoneUsageDescription ||
|
|
37
|
+
config.modResults['NSMicrophoneUsageDescription'] ||
|
|
33
38
|
MICROPHONE_USAGE;
|
|
34
39
|
if (enableNotifications) {
|
|
35
40
|
config.modResults['NSUserNotificationsUsageDescription'] =
|
|
36
|
-
|
|
41
|
+
options.iosConfig?.notificationUsageDescription ||
|
|
42
|
+
config.modResults['NSUserNotificationsUsageDescription'] ||
|
|
43
|
+
NOTIFICATION_USAGE;
|
|
37
44
|
config.modResults['NSUserNotificationAlertStyle'] = 'alert';
|
|
38
45
|
}
|
|
39
46
|
const existingBackgroundModes = config.modResults.UIBackgroundModes || [];
|
|
40
|
-
// Only add background modes if explicitly enabled
|
|
41
|
-
if (options.iosBackgroundModes?.useAudio &&
|
|
42
|
-
enableBackgroundAudio &&
|
|
47
|
+
// Only add background modes if explicitly enabled and set to true
|
|
48
|
+
if (options.iosBackgroundModes?.useAudio === true &&
|
|
49
|
+
enableBackgroundAudio === true &&
|
|
43
50
|
!existingBackgroundModes.includes('audio')) {
|
|
44
51
|
existingBackgroundModes.push('audio');
|
|
45
52
|
}
|
|
46
|
-
if (options.iosBackgroundModes?.useVoIP
|
|
53
|
+
if (options.iosBackgroundModes?.useVoIP === true &&
|
|
54
|
+
enablePhoneStateHandling === true) {
|
|
47
55
|
if (!existingBackgroundModes.includes('voip')) {
|
|
48
56
|
existingBackgroundModes.push('voip');
|
|
49
57
|
}
|
|
@@ -55,22 +63,22 @@ const withRecordingPermission = (config, props) => {
|
|
|
55
63
|
config.modResults.UIRequiredDeviceCapabilities =
|
|
56
64
|
existingCapabilities;
|
|
57
65
|
}
|
|
58
|
-
// Add additional background modes if
|
|
59
|
-
if (options.iosBackgroundModes?.useProcessing) {
|
|
66
|
+
// Add additional background modes only if explicitly set to true
|
|
67
|
+
if (options.iosBackgroundModes?.useProcessing === true) {
|
|
60
68
|
if (!existingBackgroundModes.includes('processing')) {
|
|
61
69
|
existingBackgroundModes.push('processing');
|
|
62
70
|
}
|
|
63
71
|
// Add processing info if enabled
|
|
64
72
|
config.modResults.BGTaskSchedulerPermittedIdentifiers = [
|
|
65
|
-
'com.siteed.audiostream.processing'
|
|
73
|
+
'com.siteed.audiostream.processing',
|
|
66
74
|
];
|
|
67
75
|
}
|
|
68
|
-
if (options.iosBackgroundModes?.useLocation) {
|
|
76
|
+
if (options.iosBackgroundModes?.useLocation === true) {
|
|
69
77
|
if (!existingBackgroundModes.includes('location')) {
|
|
70
78
|
existingBackgroundModes.push('location');
|
|
71
79
|
}
|
|
72
80
|
}
|
|
73
|
-
if (options.iosBackgroundModes?.useExternalAccessory) {
|
|
81
|
+
if (options.iosBackgroundModes?.useExternalAccessory === true) {
|
|
74
82
|
if (!existingBackgroundModes.includes('external-accessory')) {
|
|
75
83
|
existingBackgroundModes.push('external-accessory');
|
|
76
84
|
}
|
|
@@ -84,7 +92,7 @@ const withRecordingPermission = (config, props) => {
|
|
|
84
92
|
if (options.iosConfig?.allowBackgroundAudioControls) {
|
|
85
93
|
config.modResults.UIBackgroundModes = [
|
|
86
94
|
...existingBackgroundModes,
|
|
87
|
-
'remote-notification'
|
|
95
|
+
'remote-notification',
|
|
88
96
|
];
|
|
89
97
|
config.modResults.MPNowPlayingInfoPropertyPlaybackRate = true;
|
|
90
98
|
}
|
package/plugin/src/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ function debugLog(message: string, ...args: unknown[]): void {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
interface AudioStreamPluginOptions {
|
|
20
|
-
enablePhoneStateHandling?: boolean
|
|
20
|
+
enablePhoneStateHandling?: boolean // Controls READ_PHONE_STATE permission
|
|
21
21
|
enableNotifications?: boolean
|
|
22
22
|
enableBackgroundAudio?: boolean
|
|
23
23
|
iosBackgroundModes?: {
|
|
@@ -30,6 +30,8 @@ interface AudioStreamPluginOptions {
|
|
|
30
30
|
iosConfig?: {
|
|
31
31
|
allowBackgroundAudioControls?: boolean
|
|
32
32
|
backgroundProcessingTitle?: string
|
|
33
|
+
microphoneUsageDescription?: string
|
|
34
|
+
notificationUsageDescription?: string
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -38,7 +40,7 @@ const withRecordingPermission: ConfigPlugin<AudioStreamPluginOptions> = (
|
|
|
38
40
|
props: AudioStreamPluginOptions | void
|
|
39
41
|
) => {
|
|
40
42
|
const options: AudioStreamPluginOptions = {
|
|
41
|
-
enablePhoneStateHandling: true,
|
|
43
|
+
enablePhoneStateHandling: true, // Default to true for backward compatibility
|
|
42
44
|
enableNotifications: true,
|
|
43
45
|
enableBackgroundAudio: true,
|
|
44
46
|
iosBackgroundModes: {
|
|
@@ -48,6 +50,10 @@ const withRecordingPermission: ConfigPlugin<AudioStreamPluginOptions> = (
|
|
|
48
50
|
useLocation: false,
|
|
49
51
|
useExternalAccessory: false,
|
|
50
52
|
},
|
|
53
|
+
iosConfig: {
|
|
54
|
+
microphoneUsageDescription: MICROPHONE_USAGE,
|
|
55
|
+
notificationUsageDescription: NOTIFICATION_USAGE,
|
|
56
|
+
},
|
|
51
57
|
...(props || {}),
|
|
52
58
|
}
|
|
53
59
|
|
|
@@ -61,13 +67,16 @@ const withRecordingPermission: ConfigPlugin<AudioStreamPluginOptions> = (
|
|
|
61
67
|
|
|
62
68
|
// iOS Configuration
|
|
63
69
|
config = withInfoPlist(config as any, (config) => {
|
|
64
|
-
//
|
|
70
|
+
// Always set the microphone usage description from options first
|
|
65
71
|
config.modResults['NSMicrophoneUsageDescription'] =
|
|
72
|
+
options.iosConfig?.microphoneUsageDescription ||
|
|
66
73
|
config.modResults['NSMicrophoneUsageDescription'] ||
|
|
67
74
|
MICROPHONE_USAGE
|
|
68
75
|
|
|
69
76
|
if (enableNotifications) {
|
|
70
77
|
config.modResults['NSUserNotificationsUsageDescription'] =
|
|
78
|
+
options.iosConfig?.notificationUsageDescription ||
|
|
79
|
+
config.modResults['NSUserNotificationsUsageDescription'] ||
|
|
71
80
|
NOTIFICATION_USAGE
|
|
72
81
|
config.modResults['NSUserNotificationAlertStyle'] = 'alert'
|
|
73
82
|
}
|
|
@@ -75,16 +84,19 @@ const withRecordingPermission: ConfigPlugin<AudioStreamPluginOptions> = (
|
|
|
75
84
|
const existingBackgroundModes =
|
|
76
85
|
config.modResults.UIBackgroundModes || []
|
|
77
86
|
|
|
78
|
-
// Only add background modes if explicitly enabled
|
|
87
|
+
// Only add background modes if explicitly enabled and set to true
|
|
79
88
|
if (
|
|
80
|
-
options.iosBackgroundModes?.useAudio &&
|
|
81
|
-
enableBackgroundAudio &&
|
|
89
|
+
options.iosBackgroundModes?.useAudio === true &&
|
|
90
|
+
enableBackgroundAudio === true &&
|
|
82
91
|
!existingBackgroundModes.includes('audio')
|
|
83
92
|
) {
|
|
84
93
|
existingBackgroundModes.push('audio')
|
|
85
94
|
}
|
|
86
95
|
|
|
87
|
-
if (
|
|
96
|
+
if (
|
|
97
|
+
options.iosBackgroundModes?.useVoIP === true &&
|
|
98
|
+
enablePhoneStateHandling === true
|
|
99
|
+
) {
|
|
88
100
|
if (!existingBackgroundModes.includes('voip')) {
|
|
89
101
|
existingBackgroundModes.push('voip')
|
|
90
102
|
}
|
|
@@ -97,24 +109,24 @@ const withRecordingPermission: ConfigPlugin<AudioStreamPluginOptions> = (
|
|
|
97
109
|
existingCapabilities
|
|
98
110
|
}
|
|
99
111
|
|
|
100
|
-
// Add additional background modes if
|
|
101
|
-
if (options.iosBackgroundModes?.useProcessing) {
|
|
112
|
+
// Add additional background modes only if explicitly set to true
|
|
113
|
+
if (options.iosBackgroundModes?.useProcessing === true) {
|
|
102
114
|
if (!existingBackgroundModes.includes('processing')) {
|
|
103
115
|
existingBackgroundModes.push('processing')
|
|
104
116
|
}
|
|
105
117
|
// Add processing info if enabled
|
|
106
118
|
config.modResults.BGTaskSchedulerPermittedIdentifiers = [
|
|
107
|
-
'com.siteed.audiostream.processing'
|
|
119
|
+
'com.siteed.audiostream.processing',
|
|
108
120
|
]
|
|
109
121
|
}
|
|
110
122
|
|
|
111
|
-
if (options.iosBackgroundModes?.useLocation) {
|
|
123
|
+
if (options.iosBackgroundModes?.useLocation === true) {
|
|
112
124
|
if (!existingBackgroundModes.includes('location')) {
|
|
113
125
|
existingBackgroundModes.push('location')
|
|
114
126
|
}
|
|
115
127
|
}
|
|
116
128
|
|
|
117
|
-
if (options.iosBackgroundModes?.useExternalAccessory) {
|
|
129
|
+
if (options.iosBackgroundModes?.useExternalAccessory === true) {
|
|
118
130
|
if (!existingBackgroundModes.includes('external-accessory')) {
|
|
119
131
|
existingBackgroundModes.push('external-accessory')
|
|
120
132
|
}
|
|
@@ -122,7 +134,7 @@ const withRecordingPermission: ConfigPlugin<AudioStreamPluginOptions> = (
|
|
|
122
134
|
|
|
123
135
|
// Configure background processing info if enabled
|
|
124
136
|
if (options.iosConfig?.backgroundProcessingTitle) {
|
|
125
|
-
config.modResults.BGProcessingTaskTitle =
|
|
137
|
+
config.modResults.BGProcessingTaskTitle =
|
|
126
138
|
options.iosConfig.backgroundProcessingTitle
|
|
127
139
|
}
|
|
128
140
|
|
|
@@ -130,7 +142,7 @@ const withRecordingPermission: ConfigPlugin<AudioStreamPluginOptions> = (
|
|
|
130
142
|
if (options.iosConfig?.allowBackgroundAudioControls) {
|
|
131
143
|
config.modResults.UIBackgroundModes = [
|
|
132
144
|
...existingBackgroundModes,
|
|
133
|
-
'remote-notification'
|
|
145
|
+
'remote-notification',
|
|
134
146
|
]
|
|
135
147
|
config.modResults.MPNowPlayingInfoPropertyPlaybackRate = true
|
|
136
148
|
}
|
|
@@ -20,6 +20,7 @@ export interface AudioStreamStatus {
|
|
|
20
20
|
durationMs: number
|
|
21
21
|
size: number
|
|
22
22
|
interval: number
|
|
23
|
+
intervalAnalysis: number
|
|
23
24
|
mimeType: string
|
|
24
25
|
compression?: CompressionInfo
|
|
25
26
|
}
|
|
@@ -36,7 +37,7 @@ export interface AudioDataEvent {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export type EncodingType = 'pcm_32bit' | 'pcm_16bit' | 'pcm_8bit'
|
|
39
|
-
export type SampleRate = 16000 | 44100 | 48000
|
|
40
|
+
export type SampleRate = 16000 | 44100 | 48000
|
|
40
41
|
export type BitDepth = 8 | 16 | 32
|
|
41
42
|
|
|
42
43
|
export type ConsoleLike = {
|
|
@@ -147,7 +148,10 @@ export interface RecordingConfig {
|
|
|
147
148
|
// Interval in milliseconds at which to emit recording data
|
|
148
149
|
interval?: number
|
|
149
150
|
|
|
150
|
-
//
|
|
151
|
+
// Interval in milliseconds at which to emit analysis data
|
|
152
|
+
intervalAnalysis?: number
|
|
153
|
+
|
|
154
|
+
// Keep the device awake while recording (default is false)
|
|
151
155
|
keepAwake?: boolean
|
|
152
156
|
|
|
153
157
|
// Show a notification during recording (default is false)
|
|
@@ -46,9 +46,11 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
46
46
|
currentDurationMs: number
|
|
47
47
|
currentSize: number
|
|
48
48
|
currentInterval: number
|
|
49
|
+
currentIntervalAnalysis: number
|
|
49
50
|
lastEmittedSize: number
|
|
50
51
|
lastEmittedTime: number
|
|
51
52
|
lastEmittedCompressionSize: number
|
|
53
|
+
lastEmittedAnalysisTime: number
|
|
52
54
|
streamUuid: string | null
|
|
53
55
|
extension: 'webm' | 'wav' = 'wav' // Default extension is 'wav'
|
|
54
56
|
recordingConfig?: RecordingConfig
|
|
@@ -87,10 +89,12 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
87
89
|
this.currentSize = 0
|
|
88
90
|
this.bitDepth = 32 // Default
|
|
89
91
|
this.currentInterval = 1000 // Default interval in ms
|
|
92
|
+
this.currentIntervalAnalysis = 500 // Default analysis interval in ms
|
|
90
93
|
this.lastEmittedSize = 0
|
|
91
94
|
this.lastEmittedTime = 0
|
|
92
95
|
this.latestPosition = 0
|
|
93
96
|
this.lastEmittedCompressionSize = 0
|
|
97
|
+
this.lastEmittedAnalysisTime = 0
|
|
94
98
|
this.streamUuid = null // Initialize UUID on first recording start
|
|
95
99
|
this.audioWorkletUrl = audioWorkletUrl
|
|
96
100
|
this.featuresExtratorUrl = featuresExtratorUrl
|
|
@@ -172,6 +176,9 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
172
176
|
this.lastEmittedSize = 0
|
|
173
177
|
this.lastEmittedTime = 0
|
|
174
178
|
this.lastEmittedCompressionSize = 0
|
|
179
|
+
this.currentInterval = recordingConfig.interval ?? 1000
|
|
180
|
+
this.currentIntervalAnalysis = recordingConfig.intervalAnalysis ?? 500
|
|
181
|
+
this.lastEmittedAnalysisTime = Date.now()
|
|
175
182
|
|
|
176
183
|
// Use custom filename if provided, otherwise fallback to timestamp
|
|
177
184
|
if (recordingConfig.filename) {
|
|
@@ -335,6 +342,7 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
335
342
|
durationMs: this.currentDurationMs,
|
|
336
343
|
size: this.currentSize,
|
|
337
344
|
interval: this.currentInterval,
|
|
345
|
+
intervalAnalysis: this.currentIntervalAnalysis,
|
|
338
346
|
mimeType: `audio/${this.extension}`,
|
|
339
347
|
compression: this.recordingConfig?.compression?.enabled
|
|
340
348
|
? {
|
package/src/WebRecorder.web.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export const InlineFeaturesExtractor = `
|
|
2
2
|
// Unique ID counter
|
|
3
3
|
let uniqueIdCounter = 0
|
|
4
|
+
let accumulatedDataPoints = [] // Move outside message handler
|
|
5
|
+
let lastEmitTime = Date.now() // Move outside message handler
|
|
4
6
|
|
|
5
7
|
self.onmessage = function (event) {
|
|
6
8
|
const {
|
|
@@ -12,6 +14,7 @@ self.onmessage = function (event) {
|
|
|
12
14
|
fullAudioDurationMs,
|
|
13
15
|
numberOfChannels,
|
|
14
16
|
features: _features,
|
|
17
|
+
intervalAnalysis = 500, // Use intervalAnalysis instead of interval
|
|
15
18
|
} = event.data
|
|
16
19
|
const features = _features || {}
|
|
17
20
|
|
|
@@ -295,10 +298,24 @@ self.onmessage = function (event) {
|
|
|
295
298
|
pointsPerSecond,
|
|
296
299
|
algorithm
|
|
297
300
|
)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
301
|
+
|
|
302
|
+
// Accumulate data points
|
|
303
|
+
accumulatedDataPoints = accumulatedDataPoints.concat(result.dataPoints)
|
|
304
|
+
|
|
305
|
+
const currentTime = Date.now()
|
|
306
|
+
const shouldEmitAccumulated = currentTime - lastEmitTime >= intervalAnalysis
|
|
307
|
+
|
|
308
|
+
if (shouldEmitAccumulated) {
|
|
309
|
+
self.postMessage({
|
|
310
|
+
command: 'features',
|
|
311
|
+
result: {
|
|
312
|
+
...result,
|
|
313
|
+
dataPoints: accumulatedDataPoints
|
|
314
|
+
}
|
|
315
|
+
})
|
|
316
|
+
accumulatedDataPoints = [] // Reset accumulator
|
|
317
|
+
lastEmitTime = currentTime
|
|
318
|
+
}
|
|
302
319
|
} catch (error) {
|
|
303
320
|
console.error('[AudioFeaturesExtractor] Error in processing', error)
|
|
304
321
|
self.postMessage({ error: error.message })
|