@siteed/expo-audio-studio 2.9.0 → 2.10.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.
- package/CHANGELOG.md +13 -1
- package/android/build.gradle +9 -0
- package/android/src/androidTest/assets/chorus.wav +0 -0
- package/android/src/androidTest/assets/jfk.wav +0 -0
- package/android/src/androidTest/assets/osr_us_000_0010_8k.wav +0 -0
- package/android/src/androidTest/assets/recorder_hello_world.wav +0 -0
- package/android/src/androidTest/java/net/siteed/audiostream/AudioProcessorInstrumentedTest.kt +197 -0
- package/android/src/androidTest/java/net/siteed/audiostream/AudioRecorderInstrumentedTest.kt +541 -0
- package/android/src/androidTest/java/net/siteed/audiostream/integration/BufferDurationIntegrationTest.kt +324 -0
- package/android/src/androidTest/java/net/siteed/audiostream/integration/OutputControlIntegrationTest.kt +340 -0
- package/android/src/androidTest/java/net/siteed/audiostream/integration/README.md +95 -0
- package/android/src/androidTest/java/net/siteed/audiostream/integration/run_integration_tests.sh +28 -0
- package/android/src/main/java/net/siteed/audiostream/AudioFormatUtils.kt +264 -13
- package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +3 -13
- package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +118 -55
- package/android/src/main/java/net/siteed/audiostream/LogUtils.kt +32 -4
- package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +50 -15
- package/android/src/test/java/net/siteed/audiostream/AudioFileHandlerTest.kt +279 -0
- package/android/src/test/java/net/siteed/audiostream/AudioFormatUtilsTest.kt +273 -0
- package/android/src/test/resources/chorus.wav +0 -0
- package/android/src/test/resources/generate_test_audio.py +94 -0
- package/android/src/test/resources/jfk.wav +0 -0
- package/android/src/test/resources/osr_us_000_0010_8k.wav +0 -0
- package/android/src/test/resources/recorder_hello_world.wav +0 -0
- package/build/cjs/ExpoAudioStream.types.js.map +1 -1
- package/build/cjs/ExpoAudioStream.web.js +37 -34
- package/build/cjs/ExpoAudioStream.web.js.map +1 -1
- package/build/cjs/WebRecorder.web.js +12 -10
- package/build/cjs/WebRecorder.web.js.map +1 -1
- package/build/cjs/useAudioRecorder.js.map +1 -1
- package/build/esm/ExpoAudioStream.types.js.map +1 -1
- package/build/esm/ExpoAudioStream.web.js +37 -34
- package/build/esm/ExpoAudioStream.web.js.map +1 -1
- package/build/esm/WebRecorder.web.js +12 -10
- package/build/esm/WebRecorder.web.js.map +1 -1
- package/build/esm/useAudioRecorder.js.map +1 -1
- package/build/types/ExpoAudioStream.types.d.ts +54 -22
- package/build/types/ExpoAudioStream.types.d.ts.map +1 -1
- package/build/types/ExpoAudioStream.web.d.ts.map +1 -1
- package/build/types/WebRecorder.web.d.ts.map +1 -1
- package/ios/AudioNotificationManager.swift +2 -6
- package/ios/AudioStreamManager.swift +116 -50
- package/ios/ExpoAudioStream.podspec +6 -0
- package/ios/ExpoAudioStreamModule.swift +11 -8
- package/ios/ExpoAudioStudioTests/AudioFileHandlerTests.swift +338 -0
- package/ios/ExpoAudioStudioTests/AudioFormatUtilsTests.swift +331 -0
- package/ios/ExpoAudioStudioTests/AudioTestHelpers.swift +130 -0
- package/ios/ExpoAudioStudioTests/Info.plist +22 -0
- package/ios/ExpoAudioStudioTests/SimpleAudioTest.swift +98 -0
- package/ios/ExpoAudioStudioTests/TestAudioGenerator.swift +75 -0
- package/ios/RecordingSettings.swift +53 -22
- package/ios/tests/integration/buffer_duration_test.swift +185 -0
- package/ios/tests/integration/output_control_test.swift +322 -0
- package/ios/tests/integration/run_integration_tests.sh +27 -0
- package/ios/tests/standalone/audio_processing_test.swift +144 -0
- package/ios/tests/standalone/audio_recording_test.swift +277 -0
- package/ios/tests/standalone/audio_streaming_test.swift +249 -0
- package/ios/tests/standalone/standalone_test.swift +144 -0
- package/package.json +140 -133
- package/src/ExpoAudioStream.types.ts +66 -22
- package/src/ExpoAudioStream.web.ts +43 -38
- package/src/WebRecorder.web.ts +13 -10
- package/src/useAudioRecorder.tsx +1 -1
- package/android/src/main/test/java/net/siteed/audiostream/AudioProcessorTest.kt +0 -56
- package/ios/siteedexpoaudiostudio.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/ios/siteedexpoaudiostudio.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- /package/plugin/build/{index.d.ts → index.d.cts} +0 -0
package/package.json
CHANGED
|
@@ -1,134 +1,141 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
"
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
"
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
2
|
+
"name": "@siteed/expo-audio-studio",
|
|
3
|
+
"version": "2.10.1",
|
|
4
|
+
"description": "Comprehensive audio processing library for React Native and Expo with recording, analysis, visualization, and streaming capabilities across iOS, Android, and web",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"main": "./build/cjs/index.js",
|
|
8
|
+
"module": "./build/esm/index.js",
|
|
9
|
+
"types": "./build/types/index.d.ts",
|
|
10
|
+
"expo": {
|
|
11
|
+
"plugin": "./app.plugin.js"
|
|
12
|
+
},
|
|
13
|
+
"author": "Arthur Breton <abreton@siteed.net> (https://github.com/deeeed)",
|
|
14
|
+
"homepage": "https://github.com/deeeed/expo-audio-stream/blob/main/packages/expo-audio-studio/README.md",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/deeeed/expo-audio-stream.git",
|
|
18
|
+
"directory": "packages/expo-audio-studio"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/deeeed/expo-audio-stream/issues"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"react-native",
|
|
25
|
+
"expo",
|
|
26
|
+
"audio",
|
|
27
|
+
"recording",
|
|
28
|
+
"audio-analysis",
|
|
29
|
+
"audio-processing",
|
|
30
|
+
"audio-visualization",
|
|
31
|
+
"waveform",
|
|
32
|
+
"spectrogram",
|
|
33
|
+
"mel-spectrogram",
|
|
34
|
+
"mfcc",
|
|
35
|
+
"audio-features",
|
|
36
|
+
"audio-compression",
|
|
37
|
+
"opus",
|
|
38
|
+
"aac",
|
|
39
|
+
"pcm",
|
|
40
|
+
"wav",
|
|
41
|
+
"cross-platform",
|
|
42
|
+
"background-recording",
|
|
43
|
+
"audio-trimming",
|
|
44
|
+
"dual-stream"
|
|
45
|
+
],
|
|
46
|
+
"files": [
|
|
47
|
+
"src",
|
|
48
|
+
"android",
|
|
49
|
+
"ios",
|
|
50
|
+
"cpp",
|
|
51
|
+
"plugin",
|
|
52
|
+
"app.plugin.js",
|
|
53
|
+
"LICENSE",
|
|
54
|
+
"CHANGELOG.md",
|
|
55
|
+
"generated",
|
|
56
|
+
"expo-module.config.json",
|
|
57
|
+
"README.md",
|
|
58
|
+
"package.json",
|
|
59
|
+
"*.podspec",
|
|
60
|
+
"build",
|
|
61
|
+
"!ios/build",
|
|
62
|
+
"!android/build",
|
|
63
|
+
"!android/gradle",
|
|
64
|
+
"!android/gradlew",
|
|
65
|
+
"!android/gradlew.bat",
|
|
66
|
+
"!android/local.properties",
|
|
67
|
+
"!**/__tests__",
|
|
68
|
+
"!**/__fixtures__",
|
|
69
|
+
"!**/__mocks__",
|
|
70
|
+
"!**/.*"
|
|
71
|
+
],
|
|
72
|
+
"scripts": {
|
|
73
|
+
"build": "rimraf build && yarn build:types && yarn build:cjs && yarn build:esm && yarn build:plugin",
|
|
74
|
+
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
75
|
+
"build:esm": "tsc -p tsconfig.esm.json",
|
|
76
|
+
"build:types": "tsc -p tsconfig.types.json",
|
|
77
|
+
"build:plugin": "tsc --build plugin/tsconfig.json",
|
|
78
|
+
"build:plugin:dev": "expo-module build plugin",
|
|
79
|
+
"build:dev": "expo-module build",
|
|
80
|
+
"clean": "expo-module clean && rimraf build plugin/build",
|
|
81
|
+
"lint": "expo-module lint",
|
|
82
|
+
"test": "expo-module test",
|
|
83
|
+
"test:android": "yarn test:android:unit && yarn test:android:instrumented",
|
|
84
|
+
"test:android:unit": "cd ../../apps/playground/android && ./gradlew :siteed-expo-audio-studio:test",
|
|
85
|
+
"test:android:instrumented": "cd ../../apps/playground/android && ./gradlew :siteed-expo-audio-studio:connectedAndroidTest",
|
|
86
|
+
"test:android:unit:watch": "cd ../../apps/playground/android && ./gradlew :siteed-expo-audio-studio:test --continuous",
|
|
87
|
+
"test:ios": "cd ios && xcodebuild test -workspace ExpoAudioStudio.xcworkspace -scheme ExpoAudioStudio -destination 'platform=iOS Simulator,name=iPhone 14'",
|
|
88
|
+
"test:coverage": "cd ../../apps/playground/android && ./gradlew :siteed-expo-audio-studio:jacocoTestReport",
|
|
89
|
+
"typecheck": "tsc --noEmit",
|
|
90
|
+
"docgen": "typedoc src/index.ts --plugin typedoc-plugin-markdown --readme none --out ../../documentation_site/docs/api-reference/API",
|
|
91
|
+
"prepare": "yarn build && node -e \"require('fs').renameSync('./plugin/build/index.d.ts', './plugin/build/index.d.cts')\"",
|
|
92
|
+
"prepublishOnly.disabled": "expo-module prepublishOnly",
|
|
93
|
+
"expo-module": "expo-module",
|
|
94
|
+
"open:ios": "open -a \"Xcode\" ../../apps/playground/ios",
|
|
95
|
+
"open:android": "open -a \"Android Studio\" ../../apps/playground/android",
|
|
96
|
+
"size": "bundle-size && size-limit",
|
|
97
|
+
"release": "./publish.sh"
|
|
98
|
+
},
|
|
99
|
+
"devDependencies": {
|
|
100
|
+
"@expo/config-plugins": "~10.0.0",
|
|
101
|
+
"@expo/npm-proofread": "^1.0.1",
|
|
102
|
+
"@siteed/publisher": "^0.4.18",
|
|
103
|
+
"@size-limit/preset-big-lib": "^11.1.4",
|
|
104
|
+
"@types/jest": "^29.5.12",
|
|
105
|
+
"@types/node": "^20.12.7",
|
|
106
|
+
"@types/react": "~19.0.10",
|
|
107
|
+
"@typescript-eslint/eslint-plugin": "^7.7.0",
|
|
108
|
+
"@typescript-eslint/parser": "^7.7.0",
|
|
109
|
+
"bundle-size": "^1.1.5",
|
|
110
|
+
"eslint": "^8.56.0",
|
|
111
|
+
"eslint-config-prettier": "^9.1.0",
|
|
112
|
+
"eslint-config-universe": "^12.0.0",
|
|
113
|
+
"eslint-plugin-import": "^2.29.1",
|
|
114
|
+
"eslint-plugin-prettier": "^5.1.3",
|
|
115
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
116
|
+
"eslint-plugin-react": "^7.34.1",
|
|
117
|
+
"expo": "^53.0.9",
|
|
118
|
+
"expo-module-scripts": "^4.1.7",
|
|
119
|
+
"jest": "^29.7.0",
|
|
120
|
+
"prettier": "^3.2.5",
|
|
121
|
+
"react-native": "0.79.2",
|
|
122
|
+
"rimraf": "^6.0.1",
|
|
123
|
+
"size-limit": "^11.1.4",
|
|
124
|
+
"ts-node": "^10.9.2",
|
|
125
|
+
"typedoc": "^0.27.4",
|
|
126
|
+
"typedoc-plugin-markdown": "~4.4.2",
|
|
127
|
+
"typescript": "~5.8.3"
|
|
128
|
+
},
|
|
129
|
+
"peerDependencies": {
|
|
130
|
+
"expo": "*",
|
|
131
|
+
"react": "*",
|
|
132
|
+
"react-native": "*"
|
|
133
|
+
},
|
|
134
|
+
"publishConfig": {
|
|
135
|
+
"access": "public",
|
|
136
|
+
"registry": "https://registry.npmjs.org"
|
|
137
|
+
},
|
|
138
|
+
"dependencies": {
|
|
139
|
+
"expo-modules-core": "~2.3.13"
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -209,15 +209,7 @@ export interface IOSConfig {
|
|
|
209
209
|
|
|
210
210
|
/** Web platform specific configuration options */
|
|
211
211
|
export interface WebConfig {
|
|
212
|
-
|
|
213
|
-
* Whether to store uncompressed audio data for WAV generation
|
|
214
|
-
*
|
|
215
|
-
* When true, all PCM chunks are stored in memory to create a WAV file when compression is disabled
|
|
216
|
-
* When false, uncompressed audio won't be available, but memory usage will be lower
|
|
217
|
-
*
|
|
218
|
-
* Default: true (for backward compatibility)
|
|
219
|
-
*/
|
|
220
|
-
storeUncompressedAudio?: boolean
|
|
212
|
+
// Reserved for future web-specific options
|
|
221
213
|
}
|
|
222
214
|
|
|
223
215
|
// Add new type for interruption reasons
|
|
@@ -291,6 +283,45 @@ export const DeviceDisconnectionBehavior = {
|
|
|
291
283
|
export type DeviceDisconnectionBehaviorType =
|
|
292
284
|
(typeof DeviceDisconnectionBehavior)[keyof typeof DeviceDisconnectionBehavior]
|
|
293
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Configuration for audio output files during recording
|
|
288
|
+
*/
|
|
289
|
+
export interface OutputConfig {
|
|
290
|
+
/**
|
|
291
|
+
* Configuration for the primary (uncompressed) output file
|
|
292
|
+
*/
|
|
293
|
+
primary?: {
|
|
294
|
+
/** Whether to create the primary output file (default: true) */
|
|
295
|
+
enabled?: boolean
|
|
296
|
+
/** Format for the primary output (currently only 'wav' is supported) */
|
|
297
|
+
format?: 'wav'
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Configuration for the compressed output file
|
|
302
|
+
*/
|
|
303
|
+
compressed?: {
|
|
304
|
+
/** Whether to create a compressed output file (default: false) */
|
|
305
|
+
enabled?: boolean
|
|
306
|
+
/**
|
|
307
|
+
* Format for compression
|
|
308
|
+
* - 'aac': Advanced Audio Coding - supported on all platforms
|
|
309
|
+
* - 'opus': Opus encoding - supported on Android and Web; on iOS will automatically fall back to AAC
|
|
310
|
+
*/
|
|
311
|
+
format?: 'aac' | 'opus'
|
|
312
|
+
/** Bitrate for compression in bits per second (default: 128000) */
|
|
313
|
+
bitrate?: number
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Future enhancement: Post-processing pipeline
|
|
317
|
+
// postProcessing?: {
|
|
318
|
+
// normalize?: boolean
|
|
319
|
+
// trimSilence?: boolean
|
|
320
|
+
// noiseReduction?: boolean
|
|
321
|
+
// customProcessors?: AudioProcessor[]
|
|
322
|
+
// }
|
|
323
|
+
}
|
|
324
|
+
|
|
294
325
|
export interface RecordingConfig {
|
|
295
326
|
/** Sample rate for recording in Hz (16000, 44100, or 48000) */
|
|
296
327
|
sampleRate?: SampleRate
|
|
@@ -340,19 +371,16 @@ export interface RecordingConfig {
|
|
|
340
371
|
/** Callback function to handle audio features extraction results */
|
|
341
372
|
onAudioAnalysis?: (_: AudioAnalysisEvent) => Promise<void>
|
|
342
373
|
|
|
343
|
-
/**
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
/** Bitrate for compression in bits per second */
|
|
354
|
-
bitrate?: number
|
|
355
|
-
}
|
|
374
|
+
/**
|
|
375
|
+
* Configuration for audio output files
|
|
376
|
+
*
|
|
377
|
+
* Examples:
|
|
378
|
+
* - Primary only (default): `{ primary: { enabled: true } }`
|
|
379
|
+
* - Compressed only: `{ primary: { enabled: false }, compressed: { enabled: true, format: 'aac' } }`
|
|
380
|
+
* - Both outputs: `{ compressed: { enabled: true } }`
|
|
381
|
+
* - Streaming only: `{ primary: { enabled: false } }`
|
|
382
|
+
*/
|
|
383
|
+
output?: OutputConfig
|
|
356
384
|
|
|
357
385
|
/** Whether to automatically resume recording after an interruption (default is false) */
|
|
358
386
|
autoResumeAfterInterruption?: boolean
|
|
@@ -370,6 +398,22 @@ export interface RecordingConfig {
|
|
|
370
398
|
|
|
371
399
|
/** How to handle device disconnection during recording */
|
|
372
400
|
deviceDisconnectionBehavior?: DeviceDisconnectionBehaviorType
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Buffer duration in seconds. Controls the size of audio buffers
|
|
404
|
+
* used during recording. Smaller values reduce latency but increase
|
|
405
|
+
* CPU usage. Larger values improve efficiency but increase latency.
|
|
406
|
+
*
|
|
407
|
+
* Platform Notes:
|
|
408
|
+
* - iOS/macOS: Minimum effective 0.1s, uses accumulation below
|
|
409
|
+
* - Android: Respects all sizes within hardware limits
|
|
410
|
+
* - Web: Fully configurable
|
|
411
|
+
*
|
|
412
|
+
* Default: undefined (uses platform default ~23ms at 44.1kHz)
|
|
413
|
+
* Recommended: 0.01 - 0.5 seconds
|
|
414
|
+
* Optimal iOS: >= 0.1 seconds
|
|
415
|
+
*/
|
|
416
|
+
bufferDurationSeconds?: number
|
|
373
417
|
}
|
|
374
418
|
|
|
375
419
|
export interface NotificationConfig {
|
|
@@ -285,13 +285,15 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
285
285
|
bitDepth: this.bitDepth,
|
|
286
286
|
channels: recordingConfig.channels ?? 1,
|
|
287
287
|
sampleRate: recordingConfig.sampleRate ?? 44100,
|
|
288
|
-
compression: recordingConfig.
|
|
288
|
+
compression: recordingConfig.output?.compressed?.enabled
|
|
289
289
|
? {
|
|
290
|
-
...recordingConfig.
|
|
291
|
-
bitrate:
|
|
290
|
+
...recordingConfig.output.compressed,
|
|
291
|
+
bitrate:
|
|
292
|
+
recordingConfig.output.compressed.bitrate ?? 128000,
|
|
292
293
|
size: 0,
|
|
293
294
|
mimeType: 'audio/webm',
|
|
294
|
-
format:
|
|
295
|
+
format:
|
|
296
|
+
recordingConfig.output.compressed.format ?? 'opus',
|
|
295
297
|
compressedFileUri: '',
|
|
296
298
|
}
|
|
297
299
|
: undefined,
|
|
@@ -459,56 +461,56 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
459
461
|
let fileUri = `${this.streamUuid}.${this.extension}`
|
|
460
462
|
let mimeType = `audio/${this.extension}`
|
|
461
463
|
|
|
462
|
-
// Handle both compressed and uncompressed blobs according to configuration
|
|
463
|
-
const
|
|
464
|
-
this.recordingConfig?.
|
|
464
|
+
// Handle both compressed and uncompressed blobs according to new output configuration
|
|
465
|
+
const primaryEnabled =
|
|
466
|
+
this.recordingConfig?.output?.primary?.enabled ?? true
|
|
467
|
+
const compressedEnabled =
|
|
468
|
+
this.recordingConfig?.output?.compressed?.enabled ?? false
|
|
465
469
|
|
|
466
|
-
// Process compressed blob if available
|
|
467
|
-
if (compressedBlob) {
|
|
470
|
+
// Process compressed blob if available and enabled
|
|
471
|
+
if (compressedBlob && compressedEnabled) {
|
|
468
472
|
const compressedUri = URL.createObjectURL(compressedBlob)
|
|
469
473
|
const compressedInfo = {
|
|
470
474
|
compressedFileUri: compressedUri,
|
|
471
475
|
size: compressedBlob.size,
|
|
472
476
|
mimeType: 'audio/webm',
|
|
473
|
-
format:
|
|
477
|
+
format:
|
|
478
|
+
this.recordingConfig?.output?.compressed?.format ??
|
|
479
|
+
'opus',
|
|
474
480
|
bitrate:
|
|
475
|
-
this.recordingConfig?.
|
|
481
|
+
this.recordingConfig?.output?.compressed?.bitrate ??
|
|
482
|
+
128000,
|
|
476
483
|
}
|
|
477
484
|
|
|
478
|
-
//
|
|
479
|
-
|
|
485
|
+
// Store compression info
|
|
486
|
+
compression = compressedInfo
|
|
487
|
+
|
|
488
|
+
// If primary is disabled, use compressed as main file
|
|
489
|
+
if (!primaryEnabled) {
|
|
480
490
|
this.logger?.debug(
|
|
481
|
-
'Using compressed audio as primary output'
|
|
491
|
+
'Using compressed audio as primary output (primary disabled)'
|
|
482
492
|
)
|
|
483
493
|
fileUri = compressedUri
|
|
484
494
|
mimeType = 'audio/webm'
|
|
485
|
-
|
|
486
|
-
// Store compression info
|
|
487
|
-
compression = compressedInfo
|
|
488
|
-
} else {
|
|
489
|
-
// Compression was enabled during recording but not set as primary
|
|
490
|
-
// Store as alternate format
|
|
491
|
-
compression = compressedInfo
|
|
492
495
|
}
|
|
493
496
|
}
|
|
494
497
|
|
|
495
|
-
// Process uncompressed WAV if available
|
|
496
|
-
if (uncompressedBlob) {
|
|
498
|
+
// Process uncompressed WAV if available and primary is enabled
|
|
499
|
+
if (uncompressedBlob && primaryEnabled) {
|
|
497
500
|
const wavUri = URL.createObjectURL(uncompressedBlob)
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
fileUri = wavUri
|
|
506
|
-
mimeType = 'audio/wav'
|
|
507
|
-
}
|
|
501
|
+
fileUri = wavUri
|
|
502
|
+
mimeType = 'audio/wav'
|
|
503
|
+
} else if (!primaryEnabled && !compressedEnabled) {
|
|
504
|
+
// No outputs enabled - streaming only mode
|
|
505
|
+
this.logger?.debug('No outputs enabled - streaming only mode')
|
|
506
|
+
fileUri = ''
|
|
507
|
+
mimeType = 'audio/wav'
|
|
508
508
|
}
|
|
509
509
|
|
|
510
510
|
// Use the stored streamUuid for the final filename
|
|
511
|
-
const filename =
|
|
511
|
+
const filename = fileUri
|
|
512
|
+
? `${this.streamUuid}.${this.extension}`
|
|
513
|
+
: 'stream-only'
|
|
512
514
|
const result: AudioRecording = {
|
|
513
515
|
fileUri,
|
|
514
516
|
filename,
|
|
@@ -517,7 +519,7 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
517
519
|
channels: this.recordingConfig?.channels ?? 1,
|
|
518
520
|
sampleRate: this.recordingConfig?.sampleRate ?? 44100,
|
|
519
521
|
durationMs: this.currentDurationMs,
|
|
520
|
-
size: this.currentSize,
|
|
522
|
+
size: primaryEnabled ? this.currentSize : 0,
|
|
521
523
|
mimeType,
|
|
522
524
|
compression,
|
|
523
525
|
}
|
|
@@ -630,13 +632,16 @@ export class ExpoAudioStreamWeb extends LegacyEventEmitter {
|
|
|
630
632
|
interval: this.currentInterval,
|
|
631
633
|
intervalAnalysis: this.currentIntervalAnalysis,
|
|
632
634
|
mimeType: `audio/${this.extension}`,
|
|
633
|
-
compression: this.recordingConfig?.
|
|
635
|
+
compression: this.recordingConfig?.output?.compressed?.enabled
|
|
634
636
|
? {
|
|
635
637
|
size: this.totalCompressedSize,
|
|
636
638
|
mimeType: 'audio/webm',
|
|
637
|
-
format:
|
|
639
|
+
format:
|
|
640
|
+
this.recordingConfig.output.compressed.format ??
|
|
641
|
+
'opus',
|
|
638
642
|
bitrate:
|
|
639
|
-
this.recordingConfig.
|
|
643
|
+
this.recordingConfig.output.compressed.bitrate ??
|
|
644
|
+
128000,
|
|
640
645
|
compressedFileUri: `${this.streamUuid}.webm`,
|
|
641
646
|
}
|
|
642
647
|
: undefined,
|
package/src/WebRecorder.web.ts
CHANGED
|
@@ -154,7 +154,7 @@ export class WebRecorder {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
// Initialize compressed recording if enabled
|
|
157
|
-
if (recordingConfig.
|
|
157
|
+
if (recordingConfig.output?.compressed?.enabled) {
|
|
158
158
|
this.initializeCompressedRecorder()
|
|
159
159
|
}
|
|
160
160
|
|
|
@@ -234,9 +234,9 @@ export class WebRecorder {
|
|
|
234
234
|
)
|
|
235
235
|
const samples = chunk.length // Number of samples in this chunk
|
|
236
236
|
|
|
237
|
-
// Only store PCM data if
|
|
237
|
+
// Only store PCM data if primary output is enabled
|
|
238
238
|
const shouldStoreUncompressed =
|
|
239
|
-
this.config.
|
|
239
|
+
this.config.output?.primary?.enabled ?? true
|
|
240
240
|
|
|
241
241
|
// Store PCM chunks when needed - this is for the final WAV file
|
|
242
242
|
if (shouldStoreUncompressed) {
|
|
@@ -275,9 +275,12 @@ export class WebRecorder {
|
|
|
275
275
|
size: this.pendingCompressedChunk.size,
|
|
276
276
|
totalSize: this.compressedSize,
|
|
277
277
|
mimeType: 'audio/webm',
|
|
278
|
-
format:
|
|
278
|
+
format:
|
|
279
|
+
this.config.output?.compressed?.format ??
|
|
280
|
+
'opus',
|
|
279
281
|
bitrate:
|
|
280
|
-
this.config.
|
|
282
|
+
this.config.output?.compressed?.bitrate ??
|
|
283
|
+
128000,
|
|
281
284
|
}
|
|
282
285
|
: undefined
|
|
283
286
|
|
|
@@ -312,11 +315,11 @@ export class WebRecorder {
|
|
|
312
315
|
interval,
|
|
313
316
|
position: this.position,
|
|
314
317
|
deviceId: this.config.deviceId ?? 'default',
|
|
315
|
-
compression: this.config.
|
|
318
|
+
compression: this.config.output?.compressed
|
|
316
319
|
? {
|
|
317
|
-
enabled: this.config.
|
|
318
|
-
format: this.config.
|
|
319
|
-
bitrate: this.config.
|
|
320
|
+
enabled: this.config.output.compressed.enabled,
|
|
321
|
+
format: this.config.output.compressed.format,
|
|
322
|
+
bitrate: this.config.output.compressed.bitrate,
|
|
320
323
|
}
|
|
321
324
|
: 'disabled',
|
|
322
325
|
})
|
|
@@ -843,7 +846,7 @@ export class WebRecorder {
|
|
|
843
846
|
{
|
|
844
847
|
mimeType,
|
|
845
848
|
audioBitsPerSecond:
|
|
846
|
-
this.config.
|
|
849
|
+
this.config.output?.compressed?.bitrate ?? 128000,
|
|
847
850
|
}
|
|
848
851
|
)
|
|
849
852
|
|
package/src/useAudioRecorder.tsx
CHANGED
|
@@ -566,7 +566,7 @@ export function useAudioRecorder({
|
|
|
566
566
|
}, [dispatch])
|
|
567
567
|
|
|
568
568
|
useEffect(() => {
|
|
569
|
-
let intervalId:
|
|
569
|
+
let intervalId: ReturnType<typeof setInterval> | undefined
|
|
570
570
|
|
|
571
571
|
if (state.isRecording || state.isPaused) {
|
|
572
572
|
// Immediately check status when starting
|