@siteed/expo-audio-stream 2.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/README.md +46 -27
  2. package/build/index.d.ts +11 -12
  3. package/build/index.js +44 -10
  4. package/package.json +49 -110
  5. package/src/index.ts +18 -33
  6. package/CHANGELOG.md +0 -195
  7. package/android/build.gradle +0 -105
  8. package/android/src/main/AndroidManifest.xml +0 -27
  9. package/android/src/main/java/net/siteed/audiostream/AudioAnalysisData.kt +0 -166
  10. package/android/src/main/java/net/siteed/audiostream/AudioDataEncoder.kt +0 -9
  11. package/android/src/main/java/net/siteed/audiostream/AudioFileHandler.kt +0 -131
  12. package/android/src/main/java/net/siteed/audiostream/AudioFormatUtils.kt +0 -103
  13. package/android/src/main/java/net/siteed/audiostream/AudioNotificationsManager.kt +0 -435
  14. package/android/src/main/java/net/siteed/audiostream/AudioProcessor.kt +0 -1936
  15. package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +0 -1437
  16. package/android/src/main/java/net/siteed/audiostream/AudioRecordingService.kt +0 -138
  17. package/android/src/main/java/net/siteed/audiostream/Constants.kt +0 -20
  18. package/android/src/main/java/net/siteed/audiostream/EventSender.kt +0 -7
  19. package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +0 -509
  20. package/android/src/main/java/net/siteed/audiostream/FFT.kt +0 -99
  21. package/android/src/main/java/net/siteed/audiostream/Features.kt +0 -98
  22. package/android/src/main/java/net/siteed/audiostream/NotificationConfig.kt +0 -70
  23. package/android/src/main/java/net/siteed/audiostream/PermissionUtils.kt +0 -59
  24. package/android/src/main/java/net/siteed/audiostream/RecordingActionReceiver.kt +0 -59
  25. package/android/src/main/java/net/siteed/audiostream/RecordingConfig.kt +0 -205
  26. package/android/src/main/java/net/siteed/audiostream/WaveformConfig.kt +0 -19
  27. package/android/src/main/java/net/siteed/audiostream/WaveformRenderer.kt +0 -159
  28. package/android/src/main/res/drawable/ic_default_action_icon.xml +0 -16
  29. package/android/src/main/res/drawable/ic_microphone.xml +0 -13
  30. package/android/src/main/res/drawable/ic_pause.xml +0 -10
  31. package/android/src/main/res/drawable/ic_play.xml +0 -10
  32. package/android/src/main/res/drawable/ic_stop.xml +0 -10
  33. package/android/src/main/res/layout/notification_recording.xml +0 -37
  34. package/android/src/main/test/java/net/siteed/audiostream/AudioProcessorTest.kt +0 -56
  35. package/app.plugin.js +0 -1
  36. package/build/AudioAnalysis/AudioAnalysis.types.d.ts +0 -144
  37. package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +0 -1
  38. package/build/AudioAnalysis/AudioAnalysis.types.js +0 -3
  39. package/build/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
  40. package/build/AudioAnalysis/extractAudioAnalysis.d.ts +0 -78
  41. package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +0 -1
  42. package/build/AudioAnalysis/extractAudioAnalysis.js +0 -229
  43. package/build/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
  44. package/build/AudioAnalysis/extractWaveform.d.ts +0 -8
  45. package/build/AudioAnalysis/extractWaveform.d.ts.map +0 -1
  46. package/build/AudioAnalysis/extractWaveform.js +0 -11
  47. package/build/AudioAnalysis/extractWaveform.js.map +0 -1
  48. package/build/AudioRecorder.provider.d.ts +0 -11
  49. package/build/AudioRecorder.provider.d.ts.map +0 -1
  50. package/build/AudioRecorder.provider.js +0 -37
  51. package/build/AudioRecorder.provider.js.map +0 -1
  52. package/build/ExpoAudioStream.native.d.ts +0 -3
  53. package/build/ExpoAudioStream.native.d.ts.map +0 -1
  54. package/build/ExpoAudioStream.native.js +0 -6
  55. package/build/ExpoAudioStream.native.js.map +0 -1
  56. package/build/ExpoAudioStream.types.d.ts +0 -206
  57. package/build/ExpoAudioStream.types.d.ts.map +0 -1
  58. package/build/ExpoAudioStream.types.js +0 -2
  59. package/build/ExpoAudioStream.types.js.map +0 -1
  60. package/build/ExpoAudioStream.web.d.ts +0 -59
  61. package/build/ExpoAudioStream.web.d.ts.map +0 -1
  62. package/build/ExpoAudioStream.web.js +0 -285
  63. package/build/ExpoAudioStream.web.js.map +0 -1
  64. package/build/ExpoAudioStreamModule.d.ts +0 -3
  65. package/build/ExpoAudioStreamModule.d.ts.map +0 -1
  66. package/build/ExpoAudioStreamModule.js +0 -239
  67. package/build/ExpoAudioStreamModule.js.map +0 -1
  68. package/build/WebRecorder.web.d.ts +0 -119
  69. package/build/WebRecorder.web.d.ts.map +0 -1
  70. package/build/WebRecorder.web.js +0 -436
  71. package/build/WebRecorder.web.js.map +0 -1
  72. package/build/constants.d.ts +0 -11
  73. package/build/constants.d.ts.map +0 -1
  74. package/build/constants.js +0 -14
  75. package/build/constants.js.map +0 -1
  76. package/build/events.d.ts +0 -26
  77. package/build/events.d.ts.map +0 -1
  78. package/build/events.js +0 -21
  79. package/build/events.js.map +0 -1
  80. package/build/index.d.ts.map +0 -1
  81. package/build/index.js.map +0 -1
  82. package/build/useAudioRecorder.d.ts +0 -21
  83. package/build/useAudioRecorder.d.ts.map +0 -1
  84. package/build/useAudioRecorder.js +0 -427
  85. package/build/useAudioRecorder.js.map +0 -1
  86. package/build/utils/BlobFix.d.ts +0 -9
  87. package/build/utils/BlobFix.d.ts.map +0 -1
  88. package/build/utils/BlobFix.js +0 -498
  89. package/build/utils/BlobFix.js.map +0 -1
  90. package/build/utils/audioProcessing.d.ts +0 -24
  91. package/build/utils/audioProcessing.d.ts.map +0 -1
  92. package/build/utils/audioProcessing.js +0 -133
  93. package/build/utils/audioProcessing.js.map +0 -1
  94. package/build/utils/concatenateBuffers.d.ts +0 -8
  95. package/build/utils/concatenateBuffers.d.ts.map +0 -1
  96. package/build/utils/concatenateBuffers.js +0 -21
  97. package/build/utils/concatenateBuffers.js.map +0 -1
  98. package/build/utils/convertPCMToFloat32.d.ts +0 -13
  99. package/build/utils/convertPCMToFloat32.d.ts.map +0 -1
  100. package/build/utils/convertPCMToFloat32.js +0 -120
  101. package/build/utils/convertPCMToFloat32.js.map +0 -1
  102. package/build/utils/encodingToBitDepth.d.ts +0 -5
  103. package/build/utils/encodingToBitDepth.d.ts.map +0 -1
  104. package/build/utils/encodingToBitDepth.js +0 -13
  105. package/build/utils/encodingToBitDepth.js.map +0 -1
  106. package/build/utils/getWavFileInfo.d.ts +0 -26
  107. package/build/utils/getWavFileInfo.d.ts.map +0 -1
  108. package/build/utils/getWavFileInfo.js +0 -92
  109. package/build/utils/getWavFileInfo.js.map +0 -1
  110. package/build/utils/writeWavHeader.d.ts +0 -49
  111. package/build/utils/writeWavHeader.d.ts.map +0 -1
  112. package/build/utils/writeWavHeader.js +0 -91
  113. package/build/utils/writeWavHeader.js.map +0 -1
  114. package/build/workers/InlineFeaturesExtractor.web.d.ts +0 -2
  115. package/build/workers/InlineFeaturesExtractor.web.d.ts.map +0 -1
  116. package/build/workers/InlineFeaturesExtractor.web.js +0 -828
  117. package/build/workers/InlineFeaturesExtractor.web.js.map +0 -1
  118. package/build/workers/inlineAudioWebWorker.web.d.ts +0 -2
  119. package/build/workers/inlineAudioWebWorker.web.d.ts.map +0 -1
  120. package/build/workers/inlineAudioWebWorker.web.js +0 -157
  121. package/build/workers/inlineAudioWebWorker.web.js.map +0 -1
  122. package/expo-module.config.json +0 -9
  123. package/ios/AudioAnalysisData.swift +0 -74
  124. package/ios/AudioNotificationManager.swift +0 -135
  125. package/ios/AudioProcessingHelpers.swift +0 -743
  126. package/ios/AudioProcessor.swift +0 -858
  127. package/ios/AudioStreamError.swift +0 -7
  128. package/ios/AudioStreamManager.swift +0 -1708
  129. package/ios/AudioStreamManagerDelegate.swift +0 -16
  130. package/ios/DataPoint.swift +0 -54
  131. package/ios/DecodingConfig.swift +0 -47
  132. package/ios/ExpoAudioStream.podspec +0 -27
  133. package/ios/ExpoAudioStreamModule.swift +0 -698
  134. package/ios/FFT.swift +0 -62
  135. package/ios/Features.swift +0 -95
  136. package/ios/Logger.swift +0 -7
  137. package/ios/NotificationExtension.swift +0 -15
  138. package/ios/RecordingResult.swift +0 -22
  139. package/ios/RecordingSettings.swift +0 -265
  140. package/ios/WaveformExtractor.swift +0 -105
  141. package/plugin/build/index.d.ts +0 -21
  142. package/plugin/build/index.js +0 -191
  143. package/plugin/src/index.ts +0 -278
  144. package/plugin/tsconfig.json +0 -10
  145. package/plugin/tsconfig.tsbuildinfo +0 -1
  146. package/src/AudioAnalysis/AudioAnalysis.types.ts +0 -165
  147. package/src/AudioAnalysis/extractAudioAnalysis.ts +0 -370
  148. package/src/AudioAnalysis/extractWaveform.ts +0 -22
  149. package/src/AudioRecorder.provider.tsx +0 -54
  150. package/src/ExpoAudioStream.native.ts +0 -6
  151. package/src/ExpoAudioStream.types.ts +0 -329
  152. package/src/ExpoAudioStream.web.ts +0 -359
  153. package/src/ExpoAudioStreamModule.ts +0 -286
  154. package/src/WebRecorder.web.ts +0 -580
  155. package/src/constants.ts +0 -18
  156. package/src/events.ts +0 -60
  157. package/src/useAudioRecorder.tsx +0 -620
  158. package/src/utils/BlobFix.ts +0 -559
  159. package/src/utils/audioProcessing.ts +0 -205
  160. package/src/utils/concatenateBuffers.ts +0 -24
  161. package/src/utils/convertPCMToFloat32.ts +0 -170
  162. package/src/utils/encodingToBitDepth.ts +0 -18
  163. package/src/utils/getWavFileInfo.ts +0 -132
  164. package/src/utils/writeWavHeader.ts +0 -114
  165. package/src/workers/InlineFeaturesExtractor.web.tsx +0 -827
  166. 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;