@siteed/expo-audio-studio 2.8.6 → 2.10.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 +17 -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 -15
- 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/AudioAnalysis/AudioAnalysis.types.js.map +1 -1
- package/build/cjs/ExpoAudioStream.types.js.map +1 -1
- package/build/cjs/ExpoAudioStream.web.js +38 -35
- package/build/cjs/ExpoAudioStream.web.js.map +1 -1
- package/build/cjs/WebRecorder.web.js +122 -102
- package/build/cjs/WebRecorder.web.js.map +1 -1
- package/build/esm/AudioAnalysis/AudioAnalysis.types.js.map +1 -1
- package/build/esm/ExpoAudioStream.types.js.map +1 -1
- package/build/esm/ExpoAudioStream.web.js +38 -35
- package/build/esm/ExpoAudioStream.web.js.map +1 -1
- package/build/esm/WebRecorder.web.js +122 -102
- package/build/esm/WebRecorder.web.js.map +1 -1
- package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts +3 -1
- package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts.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 +19 -3
- 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/AudioAnalysis/AudioAnalysis.types.ts +8 -1
- package/src/ExpoAudioStream.types.ts +66 -22
- package/src/ExpoAudioStream.web.ts +45 -39
- package/src/WebRecorder.web.ts +164 -130
- 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
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env swift
|
|
2
|
+
|
|
3
|
+
import Foundation
|
|
4
|
+
import AVFoundation
|
|
5
|
+
|
|
6
|
+
// Simple test framework
|
|
7
|
+
struct TestResult {
|
|
8
|
+
let name: String
|
|
9
|
+
let passed: Bool
|
|
10
|
+
let message: String
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class SimpleTest {
|
|
14
|
+
var results: [TestResult] = []
|
|
15
|
+
|
|
16
|
+
func assert(_ condition: Bool, _ message: String, file: String = #file, line: Int = #line) {
|
|
17
|
+
let testName = "\(file.split(separator: "/").last ?? ""):\(line)"
|
|
18
|
+
results.append(TestResult(name: testName, passed: condition, message: message))
|
|
19
|
+
if !condition {
|
|
20
|
+
print("❌ FAILED: \(message) at \(testName)")
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func assertEqual<T: Equatable>(_ a: T, _ b: T, _ message: String = "", file: String = #file, line: Int = #line) {
|
|
25
|
+
let passed = a == b
|
|
26
|
+
let msg = message.isEmpty ? "\(a) should equal \(b)" : message
|
|
27
|
+
assert(passed, msg, file: file, line: line)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
func run() {
|
|
31
|
+
print("🧪 Running iOS Audio Tests...\n")
|
|
32
|
+
|
|
33
|
+
testWAVHeader()
|
|
34
|
+
testAudioBuffer()
|
|
35
|
+
|
|
36
|
+
// Print summary
|
|
37
|
+
let passed = results.filter { $0.passed }.count
|
|
38
|
+
let total = results.count
|
|
39
|
+
|
|
40
|
+
print("\n📊 Test Summary:")
|
|
41
|
+
print(" Total: \(total)")
|
|
42
|
+
print(" Passed: \(passed)")
|
|
43
|
+
print(" Failed: \(total - passed)")
|
|
44
|
+
|
|
45
|
+
if passed == total {
|
|
46
|
+
print("\n✅ All tests passed!")
|
|
47
|
+
} else {
|
|
48
|
+
print("\n❌ Some tests failed!")
|
|
49
|
+
exit(1)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
func testWAVHeader() {
|
|
54
|
+
print("Testing WAV header creation...")
|
|
55
|
+
|
|
56
|
+
let sampleRate = 44100
|
|
57
|
+
let channels = 2
|
|
58
|
+
let bitsPerSample = 16
|
|
59
|
+
let dataSize = 1024
|
|
60
|
+
|
|
61
|
+
// Create header
|
|
62
|
+
var header = Data()
|
|
63
|
+
|
|
64
|
+
// RIFF chunk
|
|
65
|
+
header.append("RIFF".data(using: .ascii)!)
|
|
66
|
+
var fileSize = UInt32(dataSize + 36).littleEndian
|
|
67
|
+
header.append(Data(bytes: &fileSize, count: 4))
|
|
68
|
+
header.append("WAVE".data(using: .ascii)!)
|
|
69
|
+
|
|
70
|
+
// fmt chunk
|
|
71
|
+
header.append("fmt ".data(using: .ascii)!)
|
|
72
|
+
var fmtSize = UInt32(16).littleEndian
|
|
73
|
+
header.append(Data(bytes: &fmtSize, count: 4))
|
|
74
|
+
var audioFormat = UInt16(1).littleEndian
|
|
75
|
+
header.append(Data(bytes: &audioFormat, count: 2))
|
|
76
|
+
var numChannels = UInt16(channels).littleEndian
|
|
77
|
+
header.append(Data(bytes: &numChannels, count: 2))
|
|
78
|
+
var sampleRateValue = UInt32(sampleRate).littleEndian
|
|
79
|
+
header.append(Data(bytes: &sampleRateValue, count: 4))
|
|
80
|
+
let byteRate = sampleRate * channels * (bitsPerSample / 8)
|
|
81
|
+
var byteRateValue = UInt32(byteRate).littleEndian
|
|
82
|
+
header.append(Data(bytes: &byteRateValue, count: 4))
|
|
83
|
+
let blockAlign = channels * (bitsPerSample / 8)
|
|
84
|
+
var blockAlignValue = UInt16(blockAlign).littleEndian
|
|
85
|
+
header.append(Data(bytes: &blockAlignValue, count: 2))
|
|
86
|
+
var bitsPerSampleValue = UInt16(bitsPerSample).littleEndian
|
|
87
|
+
header.append(Data(bytes: &bitsPerSampleValue, count: 2))
|
|
88
|
+
|
|
89
|
+
// data chunk
|
|
90
|
+
header.append("data".data(using: .ascii)!)
|
|
91
|
+
var dataSizeValue = UInt32(dataSize).littleEndian
|
|
92
|
+
header.append(Data(bytes: &dataSizeValue, count: 4))
|
|
93
|
+
|
|
94
|
+
// Tests
|
|
95
|
+
assertEqual(header.count, 44, "WAV header should be 44 bytes")
|
|
96
|
+
|
|
97
|
+
let riffHeader = String(data: header[0..<4], encoding: .ascii)
|
|
98
|
+
assertEqual(riffHeader, "RIFF", "Should have RIFF header")
|
|
99
|
+
|
|
100
|
+
let waveFormat = String(data: header[8..<12], encoding: .ascii)
|
|
101
|
+
assertEqual(waveFormat, "WAVE", "Should have WAVE format")
|
|
102
|
+
|
|
103
|
+
print("✓ WAV header test completed")
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
func testAudioBuffer() {
|
|
107
|
+
print("\nTesting audio buffer creation...")
|
|
108
|
+
|
|
109
|
+
let sampleRate = 44100.0
|
|
110
|
+
let duration = 0.1
|
|
111
|
+
let frequency = 440.0
|
|
112
|
+
|
|
113
|
+
let frameCount = Int(sampleRate * duration)
|
|
114
|
+
let format = AVAudioFormat(standardFormatWithSampleRate: sampleRate, channels: 1)!
|
|
115
|
+
|
|
116
|
+
guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(frameCount)) else {
|
|
117
|
+
assert(false, "Failed to create audio buffer")
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
buffer.frameLength = AVAudioFrameCount(frameCount)
|
|
122
|
+
|
|
123
|
+
// Generate sine wave
|
|
124
|
+
let channelData = buffer.floatChannelData![0]
|
|
125
|
+
for frame in 0..<frameCount {
|
|
126
|
+
let phase = 2.0 * Double.pi * frequency * Double(frame) / sampleRate
|
|
127
|
+
channelData[frame] = Float(sin(phase) * 0.5)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Tests
|
|
131
|
+
assertEqual(Int(buffer.frameLength), frameCount, "Frame length should match")
|
|
132
|
+
assertEqual(buffer.format.sampleRate, sampleRate, "Sample rate should match")
|
|
133
|
+
assertEqual(Int(buffer.format.channelCount), 1, "Should have 1 channel")
|
|
134
|
+
|
|
135
|
+
let middleSample = channelData[frameCount / 4]
|
|
136
|
+
assert(abs(middleSample) > 0.001, "Middle sample should not be zero")
|
|
137
|
+
|
|
138
|
+
print("✓ Audio buffer test completed")
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Run the tests
|
|
143
|
+
let test = SimpleTest()
|
|
144
|
+
test.run()
|
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.0",
|
|
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
|
+
}
|
|
@@ -55,12 +55,17 @@ export interface AudioFeatures {
|
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Options to specify which audio features to extract.
|
|
58
|
+
* Note: Advanced features (spectral features, chromagram, pitch, etc.) are experimental,
|
|
59
|
+
* especially during live recording, due to high processing requirements.
|
|
58
60
|
*/
|
|
59
61
|
export interface AudioFeaturesOptions {
|
|
62
|
+
// Basic features - well optimized
|
|
60
63
|
energy?: boolean
|
|
61
|
-
mfcc?: boolean
|
|
62
64
|
rms?: boolean
|
|
63
65
|
zcr?: boolean
|
|
66
|
+
|
|
67
|
+
// Advanced features - experimental, may impact performance in live recording
|
|
68
|
+
mfcc?: boolean
|
|
64
69
|
spectralCentroid?: boolean
|
|
65
70
|
spectralFlatness?: boolean
|
|
66
71
|
spectralRolloff?: boolean
|
|
@@ -72,6 +77,8 @@ export interface AudioFeaturesOptions {
|
|
|
72
77
|
spectralContrast?: boolean
|
|
73
78
|
tonnetz?: boolean
|
|
74
79
|
pitch?: boolean
|
|
80
|
+
|
|
81
|
+
// Utility
|
|
75
82
|
crc32?: boolean
|
|
76
83
|
}
|
|
77
84
|
|
|
@@ -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 {
|