@siteed/expo-audio-stream 2.1.0 → 2.2.1-beta.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.
Files changed (189) hide show
  1. package/README.md +23 -260
  2. package/build/index.d.ts +11 -15
  3. package/build/index.js +54 -14
  4. package/build/src/index.d.ts +11 -0
  5. package/build/src/index.js +54 -0
  6. package/package.json +49 -110
  7. package/src/index.ts +18 -32
  8. package/CHANGELOG.md +0 -206
  9. package/android/build.gradle +0 -105
  10. package/android/src/main/AndroidManifest.xml +0 -27
  11. package/android/src/main/java/net/siteed/audiostream/AudioAnalysisData.kt +0 -166
  12. package/android/src/main/java/net/siteed/audiostream/AudioDataEncoder.kt +0 -9
  13. package/android/src/main/java/net/siteed/audiostream/AudioFileHandler.kt +0 -131
  14. package/android/src/main/java/net/siteed/audiostream/AudioFormatUtils.kt +0 -103
  15. package/android/src/main/java/net/siteed/audiostream/AudioNotificationsManager.kt +0 -435
  16. package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +0 -2235
  17. package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +0 -1437
  18. package/android/src/main/java/net/siteed/audiostream/AudioRecordingService.kt +0 -152
  19. package/android/src/main/java/net/siteed/audiostream/AudioTrimmer.kt +0 -1099
  20. package/android/src/main/java/net/siteed/audiostream/Constants.kt +0 -21
  21. package/android/src/main/java/net/siteed/audiostream/EventSender.kt +0 -7
  22. package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +0 -739
  23. package/android/src/main/java/net/siteed/audiostream/FFT.kt +0 -99
  24. package/android/src/main/java/net/siteed/audiostream/Features.kt +0 -98
  25. package/android/src/main/java/net/siteed/audiostream/NotificationConfig.kt +0 -70
  26. package/android/src/main/java/net/siteed/audiostream/PermissionUtils.kt +0 -59
  27. package/android/src/main/java/net/siteed/audiostream/RecordingActionReceiver.kt +0 -59
  28. package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +0 -205
  29. package/android/src/main/java/net/siteed/audiostream/WaveformConfig.kt +0 -19
  30. package/android/src/main/java/net/siteed/audiostream/WaveformRenderer.kt +0 -159
  31. package/android/src/main/res/drawable/ic_default_action_icon.xml +0 -16
  32. package/android/src/main/res/drawable/ic_microphone.xml +0 -13
  33. package/android/src/main/res/drawable/ic_pause.xml +0 -10
  34. package/android/src/main/res/drawable/ic_play.xml +0 -10
  35. package/android/src/main/res/drawable/ic_stop.xml +0 -10
  36. package/android/src/main/res/layout/notification_recording.xml +0 -37
  37. package/android/src/main/test/java/net/siteed/audiostream/AudioProcessorTest.kt +0 -56
  38. package/app.plugin.js +0 -1
  39. package/build/AudioAnalysis/AudioAnalysis.types.d.ts +0 -179
  40. package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +0 -1
  41. package/build/AudioAnalysis/AudioAnalysis.types.js +0 -3
  42. package/build/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
  43. package/build/AudioAnalysis/extractAudioAnalysis.d.ts +0 -68
  44. package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +0 -1
  45. package/build/AudioAnalysis/extractAudioAnalysis.js +0 -203
  46. package/build/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
  47. package/build/AudioAnalysis/extractAudioData.d.ts +0 -3
  48. package/build/AudioAnalysis/extractAudioData.d.ts.map +0 -1
  49. package/build/AudioAnalysis/extractAudioData.js +0 -5
  50. package/build/AudioAnalysis/extractAudioData.js.map +0 -1
  51. package/build/AudioAnalysis/extractMelSpectrogram.d.ts +0 -14
  52. package/build/AudioAnalysis/extractMelSpectrogram.d.ts.map +0 -1
  53. package/build/AudioAnalysis/extractMelSpectrogram.js +0 -85
  54. package/build/AudioAnalysis/extractMelSpectrogram.js.map +0 -1
  55. package/build/AudioAnalysis/extractPreview.d.ts +0 -11
  56. package/build/AudioAnalysis/extractPreview.d.ts.map +0 -1
  57. package/build/AudioAnalysis/extractPreview.js +0 -25
  58. package/build/AudioAnalysis/extractPreview.js.map +0 -1
  59. package/build/AudioAnalysis/extractWaveform.d.ts +0 -8
  60. package/build/AudioAnalysis/extractWaveform.d.ts.map +0 -1
  61. package/build/AudioAnalysis/extractWaveform.js +0 -11
  62. package/build/AudioAnalysis/extractWaveform.js.map +0 -1
  63. package/build/AudioRecorder.provider.d.ts +0 -11
  64. package/build/AudioRecorder.provider.d.ts.map +0 -1
  65. package/build/AudioRecorder.provider.js +0 -37
  66. package/build/AudioRecorder.provider.js.map +0 -1
  67. package/build/ExpoAudioStream.native.d.ts +0 -3
  68. package/build/ExpoAudioStream.native.d.ts.map +0 -1
  69. package/build/ExpoAudioStream.native.js +0 -6
  70. package/build/ExpoAudioStream.native.js.map +0 -1
  71. package/build/ExpoAudioStream.types.d.ts +0 -532
  72. package/build/ExpoAudioStream.types.d.ts.map +0 -1
  73. package/build/ExpoAudioStream.types.js +0 -2
  74. package/build/ExpoAudioStream.types.js.map +0 -1
  75. package/build/ExpoAudioStream.web.d.ts +0 -59
  76. package/build/ExpoAudioStream.web.d.ts.map +0 -1
  77. package/build/ExpoAudioStream.web.js +0 -285
  78. package/build/ExpoAudioStream.web.js.map +0 -1
  79. package/build/ExpoAudioStreamModule.d.ts +0 -3
  80. package/build/ExpoAudioStreamModule.d.ts.map +0 -1
  81. package/build/ExpoAudioStreamModule.js +0 -693
  82. package/build/ExpoAudioStreamModule.js.map +0 -1
  83. package/build/WebRecorder.web.d.ts +0 -119
  84. package/build/WebRecorder.web.d.ts.map +0 -1
  85. package/build/WebRecorder.web.js +0 -436
  86. package/build/WebRecorder.web.js.map +0 -1
  87. package/build/constants.d.ts +0 -11
  88. package/build/constants.d.ts.map +0 -1
  89. package/build/constants.js +0 -14
  90. package/build/constants.js.map +0 -1
  91. package/build/events.d.ts +0 -26
  92. package/build/events.d.ts.map +0 -1
  93. package/build/events.js +0 -21
  94. package/build/events.js.map +0 -1
  95. package/build/index.d.ts.map +0 -1
  96. package/build/index.js.map +0 -1
  97. package/build/trimAudio.d.ts +0 -25
  98. package/build/trimAudio.d.ts.map +0 -1
  99. package/build/trimAudio.js +0 -67
  100. package/build/trimAudio.js.map +0 -1
  101. package/build/useAudioRecorder.d.ts +0 -21
  102. package/build/useAudioRecorder.d.ts.map +0 -1
  103. package/build/useAudioRecorder.js +0 -427
  104. package/build/useAudioRecorder.js.map +0 -1
  105. package/build/utils/BlobFix.d.ts +0 -9
  106. package/build/utils/BlobFix.d.ts.map +0 -1
  107. package/build/utils/BlobFix.js +0 -498
  108. package/build/utils/BlobFix.js.map +0 -1
  109. package/build/utils/audioProcessing.d.ts +0 -24
  110. package/build/utils/audioProcessing.d.ts.map +0 -1
  111. package/build/utils/audioProcessing.js +0 -133
  112. package/build/utils/audioProcessing.js.map +0 -1
  113. package/build/utils/concatenateBuffers.d.ts +0 -8
  114. package/build/utils/concatenateBuffers.d.ts.map +0 -1
  115. package/build/utils/concatenateBuffers.js +0 -21
  116. package/build/utils/concatenateBuffers.js.map +0 -1
  117. package/build/utils/convertPCMToFloat32.d.ts +0 -13
  118. package/build/utils/convertPCMToFloat32.d.ts.map +0 -1
  119. package/build/utils/convertPCMToFloat32.js +0 -120
  120. package/build/utils/convertPCMToFloat32.js.map +0 -1
  121. package/build/utils/encodingToBitDepth.d.ts +0 -5
  122. package/build/utils/encodingToBitDepth.d.ts.map +0 -1
  123. package/build/utils/encodingToBitDepth.js +0 -13
  124. package/build/utils/encodingToBitDepth.js.map +0 -1
  125. package/build/utils/getWavFileInfo.d.ts +0 -26
  126. package/build/utils/getWavFileInfo.d.ts.map +0 -1
  127. package/build/utils/getWavFileInfo.js +0 -92
  128. package/build/utils/getWavFileInfo.js.map +0 -1
  129. package/build/utils/writeWavHeader.d.ts +0 -49
  130. package/build/utils/writeWavHeader.d.ts.map +0 -1
  131. package/build/utils/writeWavHeader.js +0 -91
  132. package/build/utils/writeWavHeader.js.map +0 -1
  133. package/build/workers/InlineFeaturesExtractor.web.d.ts +0 -2
  134. package/build/workers/InlineFeaturesExtractor.web.d.ts.map +0 -1
  135. package/build/workers/InlineFeaturesExtractor.web.js +0 -828
  136. package/build/workers/InlineFeaturesExtractor.web.js.map +0 -1
  137. package/build/workers/inlineAudioWebWorker.web.d.ts +0 -2
  138. package/build/workers/inlineAudioWebWorker.web.d.ts.map +0 -1
  139. package/build/workers/inlineAudioWebWorker.web.js +0 -157
  140. package/build/workers/inlineAudioWebWorker.web.js.map +0 -1
  141. package/expo-module.config.json +0 -9
  142. package/ios/AudioAnalysisData.swift +0 -74
  143. package/ios/AudioNotificationManager.swift +0 -135
  144. package/ios/AudioProcessingHelpers.swift +0 -743
  145. package/ios/AudioProcessor.swift +0 -1313
  146. package/ios/AudioStreamError.swift +0 -7
  147. package/ios/AudioStreamManager.swift +0 -1708
  148. package/ios/AudioStreamManagerDelegate.swift +0 -16
  149. package/ios/DataPoint.swift +0 -54
  150. package/ios/DecodingConfig.swift +0 -47
  151. package/ios/ExpoAudioStream.podspec +0 -27
  152. package/ios/ExpoAudioStreamModule.swift +0 -805
  153. package/ios/FFT.swift +0 -62
  154. package/ios/Features.swift +0 -95
  155. package/ios/Logger.swift +0 -7
  156. package/ios/NotificationExtension.swift +0 -15
  157. package/ios/RecordingResult.swift +0 -22
  158. package/ios/RecordingSettings.swift +0 -265
  159. package/ios/WaveformExtractor.swift +0 -105
  160. package/plugin/build/index.d.ts +0 -21
  161. package/plugin/build/index.js +0 -191
  162. package/plugin/src/index.ts +0 -278
  163. package/plugin/tsconfig.json +0 -10
  164. package/plugin/tsconfig.tsbuildinfo +0 -1
  165. package/src/AudioAnalysis/AudioAnalysis.types.ts +0 -202
  166. package/src/AudioAnalysis/extractAudioAnalysis.ts +0 -333
  167. package/src/AudioAnalysis/extractAudioData.ts +0 -6
  168. package/src/AudioAnalysis/extractMelSpectrogram.ts +0 -144
  169. package/src/AudioAnalysis/extractPreview.ts +0 -34
  170. package/src/AudioAnalysis/extractWaveform.ts +0 -22
  171. package/src/AudioRecorder.provider.tsx +0 -54
  172. package/src/ExpoAudioStream.native.ts +0 -6
  173. package/src/ExpoAudioStream.types.ts +0 -641
  174. package/src/ExpoAudioStream.web.ts +0 -359
  175. package/src/ExpoAudioStreamModule.ts +0 -967
  176. package/src/WebRecorder.web.ts +0 -580
  177. package/src/constants.ts +0 -18
  178. package/src/events.ts +0 -60
  179. package/src/trimAudio.ts +0 -90
  180. package/src/useAudioRecorder.tsx +0 -620
  181. package/src/utils/BlobFix.ts +0 -559
  182. package/src/utils/audioProcessing.ts +0 -205
  183. package/src/utils/concatenateBuffers.ts +0 -24
  184. package/src/utils/convertPCMToFloat32.ts +0 -170
  185. package/src/utils/encodingToBitDepth.ts +0 -18
  186. package/src/utils/getWavFileInfo.ts +0 -132
  187. package/src/utils/writeWavHeader.ts +0 -114
  188. package/src/workers/InlineFeaturesExtractor.web.tsx +0 -827
  189. package/src/workers/inlineAudioWebWorker.web.tsx +0 -156
package/ios/FFT.swift DELETED
@@ -1,62 +0,0 @@
1
- //
2
- // FFT.swift
3
- // Pods
4
- //
5
- // Created by Arthur Breton on 20/2/2025.
6
- //
7
-
8
- import Accelerate
9
-
10
- class FFT {
11
- private let length: Int
12
- private var setup: vDSP_DFT_Setup?
13
-
14
- init(_ length: Int) {
15
- self.length = length
16
- self.setup = vDSP_DFT_zop_CreateSetup(
17
- nil,
18
- vDSP_Length(length),
19
- vDSP_DFT_Direction.FORWARD
20
- )
21
- }
22
-
23
- deinit {
24
- if let setup = setup {
25
- vDSP_DFT_DestroySetup(setup)
26
- }
27
- }
28
-
29
- func realForward(_ data: inout [Float]) {
30
- var realIn = data
31
- var imagIn = [Float](repeating: 0.0, count: length)
32
- var realOut = [Float](repeating: 0.0, count: length)
33
- var imagOut = [Float](repeating: 0.0, count: length)
34
-
35
- // Perform FFT
36
- vDSP_DFT_Execute(setup!,
37
- &realIn,
38
- &imagIn,
39
- &realOut,
40
- &imagOut)
41
-
42
- // Ensure data array has enough space for both real and imaginary parts
43
- if data.count < 2 * length {
44
- data.append(contentsOf: [Float](repeating: 0.0, count: 2 * length - data.count))
45
- }
46
-
47
- // Combine real and imaginary parts
48
- for i in 0..<length {
49
- let j = i * 2
50
- data[j] = realOut[i]
51
- data[j + 1] = imagOut[i]
52
- }
53
- }
54
-
55
- func processSegment(_ segment: [Float]) -> [Float] {
56
- var fftData = segment.count < length ?
57
- segment + [Float](repeating: 0, count: length - segment.count) :
58
- Array(segment.prefix(length))
59
- realForward(&fftData)
60
- return fftData
61
- }
62
- }
@@ -1,95 +0,0 @@
1
- //
2
- // Features.swift
3
- // ExpoAudioStream
4
- //
5
- // Created by Arthur Breton on 23/6/2024.
6
- //
7
-
8
- import Foundation
9
-
10
- public struct Features {
11
- var energy: Float
12
- var mfcc: [Float]
13
- var rms: Float
14
- var minAmplitude: Float
15
- var maxAmplitude: Float
16
- var zcr: Float
17
- var spectralCentroid: Float
18
- var spectralFlatness: Float
19
- var spectralRollOff: Float?
20
- var spectralBandwidth: Float?
21
- var chromagram: [Float]?
22
- var tempo: Float?
23
- var hnr: Float?
24
- var melSpectrogram: [Float]?
25
- var spectralContrast: [Float]?
26
- var tonnetz: [Float]?
27
- var pitch: Float?
28
- var crc32: UInt32?
29
-
30
- init(
31
- energy: Float = 0,
32
- mfcc: [Float] = [],
33
- rms: Float = 0,
34
- minAmplitude: Float = 0,
35
- maxAmplitude: Float = 0,
36
- zcr: Float = 0,
37
- spectralCentroid: Float = 0,
38
- spectralFlatness: Float = 0,
39
- spectralRollOff: Float? = nil,
40
- spectralBandwidth: Float? = nil,
41
- chromagram: [Float]? = nil,
42
- tempo: Float? = nil,
43
- hnr: Float? = nil,
44
- melSpectrogram: [Float]? = nil,
45
- spectralContrast: [Float]? = nil,
46
- tonnetz: [Float]? = nil,
47
- pitch: Float? = nil,
48
- crc32: UInt32? = nil
49
- ) {
50
- self.energy = energy
51
- self.mfcc = mfcc
52
- self.rms = rms
53
- self.minAmplitude = minAmplitude
54
- self.maxAmplitude = maxAmplitude
55
- self.zcr = zcr
56
- self.spectralCentroid = spectralCentroid
57
- self.spectralFlatness = spectralFlatness
58
- self.spectralRollOff = spectralRollOff
59
- self.spectralBandwidth = spectralBandwidth
60
- self.chromagram = chromagram
61
- self.tempo = tempo
62
- self.hnr = hnr
63
- self.melSpectrogram = melSpectrogram
64
- self.spectralContrast = spectralContrast
65
- self.tonnetz = tonnetz
66
- self.pitch = pitch
67
- self.crc32 = crc32
68
- }
69
- }
70
-
71
- extension Features {
72
- func toDictionary() -> [String: Any] {
73
- var dict: [String: Any] = [
74
- "energy": energy,
75
- "mfcc": mfcc,
76
- "rms": rms,
77
- "minAmplitude": minAmplitude,
78
- "maxAmplitude": maxAmplitude,
79
- "zcr": zcr,
80
- "spectralCentroid": spectralCentroid,
81
- "spectralFlatness": spectralFlatness,
82
- "spectralRollOff": spectralRollOff ?? 0,
83
- "spectralBandwidth": spectralBandwidth ?? 0,
84
- "chromagram": chromagram ?? [],
85
- "tempo": tempo ?? 0,
86
- "hnr": hnr ?? 0,
87
- "melSpectrogram": melSpectrogram ?? [],
88
- "spectralContrast": spectralContrast ?? [],
89
- "tonnetz": tonnetz ?? [],
90
- "pitch": pitch ?? 0,
91
- "crc32": crc32 ?? 0
92
- ]
93
- return dict
94
- }
95
- }
package/ios/Logger.swift DELETED
@@ -1,7 +0,0 @@
1
- class Logger {
2
- static func debug(_ message: @autoclosure () -> String) {
3
- #if DEBUG
4
- print(message())
5
- #endif
6
- }
7
- }
@@ -1,15 +0,0 @@
1
- //
2
- // NotificationExtension.swift
3
- // Pods
4
- //
5
- // Created by Arthur Breton on 27/10/2024.
6
- //
7
-
8
-
9
- import Foundation
10
-
11
- extension Notification.Name {
12
- static let pauseRecording = Notification.Name("PAUSE_RECORDING")
13
- static let resumeRecording = Notification.Name("RESUME_RECORDING")
14
- static let notificationActionTriggered = Notification.Name("notificationActionTriggered")
15
- }
@@ -1,22 +0,0 @@
1
- // RecordingResult.swift
2
-
3
- struct RecordingResult {
4
- var fileUri: String
5
- var filename: String
6
- var mimeType: String
7
- var duration: Int64
8
- var size: Int64
9
- var channels: Int
10
- var bitDepth: Int
11
- var sampleRate: Double
12
- var compression: CompressedRecordingInfo?
13
- }
14
-
15
- struct StartRecordingResult {
16
- var fileUri: String
17
- var mimeType: String
18
- var channels: Int
19
- var bitDepth: Int
20
- var sampleRate: Double
21
- var compression: CompressedRecordingInfo?
22
- }
@@ -1,265 +0,0 @@
1
- // RecordingSettings.swift
2
-
3
- import AVFoundation
4
-
5
- struct NotificationAction {
6
- var title: String
7
- var identifier: String
8
- }
9
-
10
- struct IOSAudioSessionConfig {
11
- var category: AVAudioSession.Category
12
- var mode: AVAudioSession.Mode
13
- var categoryOptions: AVAudioSession.CategoryOptions
14
- }
15
-
16
- struct IOSNotificationConfig {
17
- var categoryIdentifier: String?
18
- }
19
-
20
- struct CompressedRecordingInfo {
21
- var compressedFileUri: String
22
- var mimeType: String
23
- var bitrate: Int
24
- var format: String
25
- var size: Int64 = 0 // Add size with default value
26
-
27
- static func validate(format: String, bitrate: Int) -> Result<(String, Int), Error> {
28
- // Validate format
29
- guard ["aac", "opus"].contains(format.lowercased()) else {
30
- return .failure(RecordingError.unsupportedFormat(format))
31
- }
32
-
33
- // Adjust bitrate based on format
34
- let adjustedBitrate: Int
35
- if format.lowercased() == "aac" {
36
- // Standard AAC bitrates (bps)
37
- let standardAACBitrates = [32000, 48000, 64000, 96000, 128000, 160000, 192000, 256000, 320000]
38
- adjustedBitrate = standardAACBitrates.min(by: { abs($0 - bitrate) < abs($1 - bitrate) }) ?? 128000
39
- } else {
40
- // For Opus, allow lower bitrates (especially good for voice)
41
- // Typical Opus voice bitrates: 8-24 kbps, music: 32-128 kbps
42
- adjustedBitrate = min(max(bitrate, 8000), 320000)
43
- }
44
-
45
- return .success((format, adjustedBitrate))
46
- }
47
- }
48
-
49
- struct NotificationConfig {
50
- var title: String?
51
- var text: String?
52
- var icon: String?
53
- var ios: IOSNotificationConfig?
54
- }
55
-
56
- struct IOSConfig {
57
- var audioSession: IOSAudioSessionConfig?
58
- }
59
-
60
- enum RecordingError: Error {
61
- case unsupportedFormat(String)
62
- case invalidBitrate(Int)
63
- case invalidOutputDirectory(String)
64
-
65
- var localizedDescription: String {
66
- switch self {
67
- case .unsupportedFormat(let format):
68
- return "Unsupported compression format: \(format). iOS only supports AAC."
69
- case .invalidBitrate(let bitrate):
70
- return "Invalid bitrate: \(bitrate). Must be between 8000 and 960000 bps."
71
- case .invalidOutputDirectory(let directory):
72
- return "Invalid output directory: \(directory). Directory does not exist, is not a directory, or is not writable."
73
- }
74
- }
75
- }
76
-
77
- struct RecordingSettings {
78
- // Core recording settings
79
- var sampleRate: Double
80
- var desiredSampleRate: Double
81
- var numberOfChannels: Int = 1
82
- var bitDepth: Int = 16
83
- var interval: Int?
84
- var intervalAnalysis: Int?
85
-
86
- // Feature flags
87
- var keepAwake: Bool = true
88
- var showNotification: Bool = false
89
- var enableProcessing: Bool = false
90
-
91
- // Remove pointsPerSecond and algorithm
92
- var featureOptions: [String: Bool]? = ["rms": true, "zcr": true]
93
-
94
- // iOS-specific configuration
95
- var ios: IOSConfig?
96
-
97
- // Notification configuration
98
- var notification: NotificationConfig?
99
-
100
- let enableCompressedOutput: Bool
101
- let compressedFormat: String // "aac" or "opus"
102
- let compressedBitRate: Int
103
-
104
- let autoResumeAfterInterruption: Bool
105
-
106
- var outputDirectory: String? = nil
107
- var filename: String? = nil
108
-
109
- // Update default to 100ms
110
- var segmentDurationMs: Int = 100 // Default 100ms segments
111
-
112
- static func fromDictionary(_ dict: [String: Any]) -> Result<RecordingSettings, Error> {
113
- // Extract compression settings
114
- let compression = dict["compression"] as? [String: Any]
115
- let enableCompressedOutput = compression?["enabled"] as? Bool ?? false
116
- let compressedFormat = (compression?["format"] as? String)?.lowercased() ?? "opus"
117
- let compressedBitRate = compression?["bitrate"] as? Int ?? 24000
118
-
119
- // Validate compression settings if enabled
120
- if enableCompressedOutput {
121
- // Validate format and bitrate
122
- if case .failure(let error) = CompressedRecordingInfo.validate(
123
- format: compressedFormat,
124
- bitrate: compressedBitRate
125
- ) {
126
- return .failure(error)
127
- }
128
- }
129
-
130
- // Create settings
131
- var settings = RecordingSettings(
132
- sampleRate: dict["sampleRate"] as? Double ?? 44100.0,
133
- desiredSampleRate: dict["desiredSampleRate"] as? Double ?? 44100.0,
134
- enableCompressedOutput: enableCompressedOutput,
135
- compressedFormat: compressedFormat,
136
- compressedBitRate: compressedBitRate,
137
- autoResumeAfterInterruption: dict["autoResumeAfterInterruption"] as? Bool ?? false
138
- )
139
-
140
- // Parse core settings
141
- settings.numberOfChannels = dict["channels"] as? Int ?? 1
142
- settings.bitDepth = dict["bitDepth"] as? Int ?? 16
143
- settings.interval = dict["interval"] as? Int
144
- settings.intervalAnalysis = dict["intervalAnalysis"] as? Int
145
-
146
- // Parse feature flags
147
- settings.keepAwake = dict["keepAwake"] as? Bool ?? true
148
- settings.showNotification = dict["showNotification"] as? Bool ?? false
149
- settings.enableProcessing = dict["enableProcessing"] as? Bool ?? false
150
-
151
- settings.featureOptions = dict["features"] as? [String: Bool]
152
-
153
- // Update segmentDurationMs parsing
154
- settings.segmentDurationMs = dict["segmentDurationMs"] as? Int ?? 100
155
-
156
- // Parse iOS-specific config
157
- if let iosDict = dict["ios"] as? [String: Any],
158
- let audioSessionDict = iosDict["audioSession"] as? [String: Any] {
159
-
160
- // Map category
161
- let category: AVAudioSession.Category
162
- if let categoryStr = audioSessionDict["category"] as? String {
163
- switch categoryStr {
164
- case "Ambient": category = .ambient
165
- case "SoloAmbient": category = .soloAmbient
166
- case "Playback": category = .playback
167
- case "Record": category = .record
168
- case "PlayAndRecord": category = .playAndRecord
169
- case "MultiRoute": category = .multiRoute
170
- default: category = .record
171
- }
172
- } else {
173
- category = .record
174
- }
175
-
176
- // Map mode
177
- let mode: AVAudioSession.Mode
178
- if let modeStr = audioSessionDict["mode"] as? String {
179
- switch modeStr {
180
- case "Default": mode = .default
181
- case "VoiceChat": mode = .voiceChat
182
- case "VideoChat": mode = .videoChat
183
- case "GameChat": mode = .gameChat
184
- case "VideoRecording": mode = .videoRecording
185
- case "Measurement": mode = .measurement
186
- case "MoviePlayback": mode = .moviePlayback
187
- case "SpokenAudio": mode = .spokenAudio
188
- default: mode = .default
189
- }
190
- } else {
191
- mode = .default
192
- }
193
-
194
- // Map category options
195
- var categoryOptions: AVAudioSession.CategoryOptions = []
196
- if let optionsArray = audioSessionDict["categoryOptions"] as? [String] {
197
- for option in optionsArray {
198
- switch option {
199
- case "MixWithOthers": categoryOptions.insert(.mixWithOthers)
200
- case "DuckOthers": categoryOptions.insert(.duckOthers)
201
- case "InterruptSpokenAudioAndMixWithOthers": categoryOptions.insert(.interruptSpokenAudioAndMixWithOthers)
202
- case "AllowBluetooth": categoryOptions.insert(.allowBluetooth)
203
- case "AllowBluetoothA2DP": categoryOptions.insert(.allowBluetoothA2DP)
204
- case "AllowAirPlay": categoryOptions.insert(.allowAirPlay)
205
- case "DefaultToSpeaker": categoryOptions.insert(.defaultToSpeaker)
206
- default: break
207
- }
208
- }
209
- }
210
-
211
- settings.ios = IOSConfig(audioSession: IOSAudioSessionConfig(
212
- category: category,
213
- mode: mode,
214
- categoryOptions: categoryOptions
215
- ))
216
- }
217
-
218
- // Parse notification config
219
- if let notificationDict = dict["notification"] as? [String: Any] {
220
- var notificationConfig = NotificationConfig()
221
- notificationConfig.title = notificationDict["title"] as? String
222
- notificationConfig.text = notificationDict["text"] as? String
223
- notificationConfig.icon = notificationDict["icon"] as? String
224
-
225
- // Parse iOS-specific notification config
226
- if let iosNotificationDict = notificationDict["ios"] as? [String: Any] {
227
- notificationConfig.ios = IOSNotificationConfig(
228
- categoryIdentifier: iosNotificationDict["categoryIdentifier"] as? String
229
- )
230
- }
231
-
232
- settings.notification = notificationConfig
233
- }
234
-
235
- // Parse output settings (they remain nil if not provided)
236
- if let directory = dict["outputDirectory"] as? String {
237
- // Only validate if a custom directory is provided
238
- let fileManager = FileManager.default
239
- var isDirectory: ObjCBool = false
240
-
241
- // Clean up the directory path by removing file:// protocol if present
242
- let cleanDirectory = directory.replacingOccurrences(of: "file://", with: "")
243
- .trimmingCharacters(in: CharacterSet(charactersIn: "/"))
244
- .replacingOccurrences(of: "//", with: "/")
245
-
246
- if !fileManager.fileExists(atPath: cleanDirectory, isDirectory: &isDirectory) {
247
- return .failure(RecordingError.invalidOutputDirectory("Directory does not exist: \(cleanDirectory)"))
248
- }
249
-
250
- if !isDirectory.boolValue {
251
- return .failure(RecordingError.invalidOutputDirectory("Path is not a directory: \(cleanDirectory)"))
252
- }
253
-
254
- if !fileManager.isWritableFile(atPath: cleanDirectory) {
255
- return .failure(RecordingError.invalidOutputDirectory("Directory is not writable: \(cleanDirectory)"))
256
- }
257
-
258
- settings.outputDirectory = cleanDirectory
259
- }
260
-
261
- settings.filename = dict["filename"] as? String
262
-
263
- return .success(settings)
264
- }
265
- }
@@ -1,105 +0,0 @@
1
- // WaveformExtractor.swift
2
-
3
- import Accelerate
4
- import AVFoundation
5
-
6
- /// This class is responsible for extracting waveform data from an audio file.
7
- public class WaveformExtractor {
8
- public private(set) var audioFile: AVAudioFile?
9
- private var result: (Any) -> Void
10
- private var reject: (String, String) -> Void
11
- private var waveformData = Array<Float>()
12
- private var progress: Float = 0.0
13
- private var channelCount: Int = 1
14
- private var currentProgress: Float = 0.0
15
- private let extractionQueue = DispatchQueue(label: "WaveformExtractor", attributes: .concurrent)
16
- private var _abortWaveformExtraction: Bool = false
17
-
18
- /// Indicates whether the waveform extraction process should be aborted.
19
- public var abortWaveformExtraction: Bool {
20
- get { _abortWaveformExtraction }
21
- set { _abortWaveformExtraction = newValue }
22
- }
23
-
24
- /// Initializes the waveform extractor with an audio file URL, resolve, and reject callbacks.
25
- ///
26
- /// - Parameters:
27
- /// - url: The URL of the audio file to be read.
28
- /// - resolve: The callback to be called on successful extraction.
29
- /// - reject: The callback to be called on extraction failure.
30
- public init(url: URL, resolve: @escaping (Any) -> Void, reject: @escaping (String, String) -> Void) throws {
31
- self.audioFile = try AVAudioFile(forReading: url)
32
- self.result = resolve
33
- self.reject = reject
34
- }
35
-
36
- deinit {
37
- audioFile = nil
38
- }
39
-
40
- /// Extracts the waveform data from the audio file.
41
- ///
42
- /// - Parameters:
43
- /// - numberOfSamples: The number of samples to extract for the waveform.
44
- /// - offset: The offset to start reading from.
45
- /// - length: The length of the audio to read.
46
- /// - Returns: A 2D array of floats where each sub-array represents waveform data for a specific channel.
47
- public func extractWaveform(numberOfSamples: Int?, offset: Int? = 0, length: UInt? = nil) -> [[Float]]? {
48
- guard let audioFile = audioFile else { return nil }
49
-
50
- let numberOfSamples = max(1, numberOfSamples ?? 100)
51
- let totalFrameCount = AVAudioFrameCount(audioFile.length)
52
- var framesPerBuffer = totalFrameCount / AVAudioFrameCount(numberOfSamples)
53
-
54
- guard let rmsBuffer = AVAudioPCMBuffer(pcmFormat: audioFile.processingFormat, frameCapacity: AVAudioFrameCount(framesPerBuffer)) else { return nil }
55
-
56
- channelCount = Int(audioFile.processingFormat.channelCount)
57
- var data = Array(repeating: [Float](repeating: 0, count: numberOfSamples), count: channelCount)
58
-
59
- var startFrame: AVAudioFramePosition = offset == nil ? audioFile.framePosition : Int64(offset! * Int(framesPerBuffer))
60
- var end = numberOfSamples
61
- if let length = length {
62
- end = Int(length)
63
- }
64
-
65
- for i in 0..<end {
66
- if abortWaveformExtraction {
67
- audioFile.framePosition = startFrame
68
- abortWaveformExtraction = false
69
- return nil
70
- }
71
-
72
- do {
73
- audioFile.framePosition = startFrame
74
- try audioFile.read(into: rmsBuffer, frameCount: framesPerBuffer)
75
- } catch {
76
- reject("AUDIO_READ_ERROR", "Couldn't read into buffer")
77
- return nil
78
- }
79
-
80
- guard let floatData = rmsBuffer.floatChannelData else { return nil }
81
-
82
- for channel in 0..<channelCount {
83
- var rms: Float = 0.0
84
- vDSP_rmsqv(floatData[channel], 1, &rms, vDSP_Length(rmsBuffer.frameLength))
85
- data[channel][i] = rms
86
- }
87
-
88
- currentProgress += 1
89
- progress = currentProgress / Float(numberOfSamples)
90
-
91
- startFrame += AVAudioFramePosition(framesPerBuffer)
92
- if startFrame + AVAudioFramePosition(framesPerBuffer) > AVAudioFramePosition(totalFrameCount) {
93
- framesPerBuffer = totalFrameCount - AVAudioFrameCount(startFrame)
94
- if framesPerBuffer <= 0 { break }
95
- }
96
- }
97
-
98
- return data
99
- }
100
-
101
- /// Cancels the waveform extraction process.
102
- public func cancel() {
103
- abortWaveformExtraction = true
104
- }
105
- }
@@ -1,21 +0,0 @@
1
- import { ConfigPlugin } from '@expo/config-plugins';
2
- interface AudioStreamPluginOptions {
3
- enablePhoneStateHandling?: boolean;
4
- enableNotifications?: boolean;
5
- enableBackgroundAudio?: boolean;
6
- iosBackgroundModes?: {
7
- useVoIP?: boolean;
8
- useAudio?: boolean;
9
- useProcessing?: boolean;
10
- useLocation?: boolean;
11
- useExternalAccessory?: boolean;
12
- };
13
- iosConfig?: {
14
- allowBackgroundAudioControls?: boolean;
15
- backgroundProcessingTitle?: string;
16
- microphoneUsageDescription?: string;
17
- notificationUsageDescription?: string;
18
- };
19
- }
20
- declare const withRecordingPermission: ConfigPlugin<AudioStreamPluginOptions>;
21
- export default withRecordingPermission;