@siteed/expo-audio-studio 2.7.0 → 2.8.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 +12 -1
- package/app.plugin.cjs +15 -2
- package/build/cjs/AudioAnalysis/AudioAnalysis.types.js +4 -0
- package/build/cjs/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
- package/build/cjs/AudioAnalysis/extractAudioAnalysis.js +205 -0
- package/build/cjs/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
- package/build/cjs/AudioAnalysis/extractAudioData.js +12 -0
- package/build/cjs/AudioAnalysis/extractAudioData.js.map +1 -0
- package/build/cjs/AudioAnalysis/extractMelSpectrogram.js +89 -0
- package/build/cjs/AudioAnalysis/extractMelSpectrogram.js.map +1 -0
- package/build/cjs/AudioAnalysis/extractPreview.js +29 -0
- package/build/cjs/AudioAnalysis/extractPreview.js.map +1 -0
- package/build/cjs/AudioAnalysis/extractWaveform.js +18 -0
- package/build/cjs/AudioAnalysis/extractWaveform.js.map +1 -0
- package/build/cjs/AudioDeviceManager.js +500 -0
- package/build/cjs/AudioDeviceManager.js.map +1 -0
- package/build/cjs/AudioRecorder.provider.js +68 -0
- package/build/cjs/AudioRecorder.provider.js.map +1 -0
- package/build/cjs/ExpoAudioStream.native.js +8 -0
- package/build/cjs/ExpoAudioStream.native.js.map +1 -0
- package/build/cjs/ExpoAudioStream.types.js +11 -0
- package/build/cjs/ExpoAudioStream.types.js.map +1 -0
- package/build/cjs/ExpoAudioStream.web.js +705 -0
- package/build/cjs/ExpoAudioStream.web.js.map +1 -0
- package/build/cjs/ExpoAudioStreamModule.js +718 -0
- package/build/cjs/ExpoAudioStreamModule.js.map +1 -0
- package/build/cjs/WebRecorder.web.js +756 -0
- package/build/cjs/WebRecorder.web.js.map +1 -0
- package/build/cjs/constants.js +17 -0
- package/build/cjs/constants.js.map +1 -0
- package/build/cjs/events.js +30 -0
- package/build/cjs/events.js.map +1 -0
- package/build/cjs/hooks/useAudioDevices.js +155 -0
- package/build/cjs/hooks/useAudioDevices.js.map +1 -0
- package/build/cjs/index.js +50 -0
- package/build/cjs/index.js.map +1 -0
- package/build/cjs/trimAudio.js +75 -0
- package/build/cjs/trimAudio.js.map +1 -0
- package/build/cjs/useAudioRecorder.js +453 -0
- package/build/cjs/useAudioRecorder.js.map +1 -0
- package/build/cjs/utils/BlobFix.js +502 -0
- package/build/cjs/utils/BlobFix.js.map +1 -0
- package/build/cjs/utils/audioProcessing.js +137 -0
- package/build/cjs/utils/audioProcessing.js.map +1 -0
- package/build/cjs/utils/concatenateBuffers.js +25 -0
- package/build/cjs/utils/concatenateBuffers.js.map +1 -0
- package/build/cjs/utils/convertPCMToFloat32.js +124 -0
- package/build/cjs/utils/convertPCMToFloat32.js.map +1 -0
- package/build/cjs/utils/crc32.js +52 -0
- package/build/cjs/utils/crc32.js.map +1 -0
- package/build/cjs/utils/encodingToBitDepth.js +17 -0
- package/build/cjs/utils/encodingToBitDepth.js.map +1 -0
- package/build/cjs/utils/getWavFileInfo.js +96 -0
- package/build/cjs/utils/getWavFileInfo.js.map +1 -0
- package/build/cjs/utils/writeWavHeader.js +88 -0
- package/build/cjs/utils/writeWavHeader.js.map +1 -0
- package/build/cjs/workers/InlineFeaturesExtractor.web.js +853 -0
- package/build/cjs/workers/InlineFeaturesExtractor.web.js.map +1 -0
- package/build/cjs/workers/inlineAudioWebWorker.web.js +184 -0
- package/build/cjs/workers/inlineAudioWebWorker.web.js.map +1 -0
- package/build/esm/AudioAnalysis/AudioAnalysis.types.js.map +1 -0
- package/build/esm/AudioAnalysis/extractAudioAnalysis.js.map +1 -0
- package/build/esm/AudioAnalysis/extractAudioData.js.map +1 -0
- package/build/esm/AudioAnalysis/extractMelSpectrogram.js.map +1 -0
- package/build/esm/AudioAnalysis/extractPreview.js.map +1 -0
- package/build/esm/AudioAnalysis/extractWaveform.js.map +1 -0
- package/build/esm/AudioDeviceManager.js.map +1 -0
- package/build/esm/AudioRecorder.provider.js.map +1 -0
- package/build/esm/ExpoAudioStream.native.js.map +1 -0
- package/build/esm/ExpoAudioStream.types.js.map +1 -0
- package/build/esm/ExpoAudioStream.web.js.map +1 -0
- package/build/esm/ExpoAudioStreamModule.js.map +1 -0
- package/build/esm/WebRecorder.web.js.map +1 -0
- package/build/esm/constants.js.map +1 -0
- package/build/esm/events.js.map +1 -0
- package/build/esm/hooks/useAudioDevices.js.map +1 -0
- package/build/esm/index.js.map +1 -0
- package/build/esm/trimAudio.js.map +1 -0
- package/build/esm/useAudioRecorder.js.map +1 -0
- package/build/esm/utils/BlobFix.js.map +1 -0
- package/build/esm/utils/audioProcessing.js.map +1 -0
- package/build/esm/utils/concatenateBuffers.js.map +1 -0
- package/build/esm/utils/convertPCMToFloat32.js.map +1 -0
- package/build/esm/utils/crc32.js.map +1 -0
- package/build/esm/utils/encodingToBitDepth.js.map +1 -0
- package/build/esm/utils/getWavFileInfo.js.map +1 -0
- package/build/esm/utils/writeWavHeader.js.map +1 -0
- package/build/esm/workers/InlineFeaturesExtractor.web.js.map +1 -0
- package/build/esm/workers/inlineAudioWebWorker.web.js.map +1 -0
- package/build/types/AudioAnalysis/AudioAnalysis.types.d.ts.map +1 -0
- package/build/types/AudioAnalysis/extractAudioAnalysis.d.ts.map +1 -0
- package/build/types/AudioAnalysis/extractAudioData.d.ts.map +1 -0
- package/build/types/AudioAnalysis/extractMelSpectrogram.d.ts.map +1 -0
- package/build/types/AudioAnalysis/extractPreview.d.ts.map +1 -0
- package/build/types/AudioAnalysis/extractWaveform.d.ts.map +1 -0
- package/build/types/AudioDeviceManager.d.ts.map +1 -0
- package/build/types/AudioRecorder.provider.d.ts.map +1 -0
- package/build/types/ExpoAudioStream.native.d.ts.map +1 -0
- package/build/types/ExpoAudioStream.types.d.ts.map +1 -0
- package/build/types/ExpoAudioStream.web.d.ts.map +1 -0
- package/build/types/ExpoAudioStreamModule.d.ts.map +1 -0
- package/build/types/WebRecorder.web.d.ts.map +1 -0
- package/build/types/constants.d.ts.map +1 -0
- package/build/types/events.d.ts.map +1 -0
- package/build/types/hooks/useAudioDevices.d.ts.map +1 -0
- package/build/types/index.d.ts.map +1 -0
- package/build/types/trimAudio.d.ts.map +1 -0
- package/build/types/useAudioRecorder.d.ts.map +1 -0
- package/build/types/utils/BlobFix.d.ts.map +1 -0
- package/build/types/utils/audioProcessing.d.ts.map +1 -0
- package/build/types/utils/concatenateBuffers.d.ts.map +1 -0
- package/build/types/utils/convertPCMToFloat32.d.ts.map +1 -0
- package/build/types/utils/crc32.d.ts.map +1 -0
- package/build/types/utils/encodingToBitDepth.d.ts.map +1 -0
- package/build/types/utils/getWavFileInfo.d.ts.map +1 -0
- package/build/types/utils/writeWavHeader.d.ts.map +1 -0
- package/build/types/workers/InlineFeaturesExtractor.web.d.ts.map +1 -0
- package/build/types/workers/inlineAudioWebWorker.web.d.ts.map +1 -0
- package/ios/AudioNotificationManager.swift +42 -19
- package/ios/AudioProcessingHelpers.swift +5 -5
- package/ios/AudioProcessor.swift +44 -218
- package/ios/AudioStreamManager.swift +121 -61
- package/ios/DataPoint.swift +5 -5
- package/ios/ExpoAudioStreamModule.swift +2 -1
- package/package.json +25 -7
- package/build/AudioAnalysis/AudioAnalysis.types.d.ts.map +0 -1
- package/build/AudioAnalysis/AudioAnalysis.types.js.map +0 -1
- package/build/AudioAnalysis/extractAudioAnalysis.d.ts.map +0 -1
- package/build/AudioAnalysis/extractAudioAnalysis.js.map +0 -1
- package/build/AudioAnalysis/extractAudioData.d.ts.map +0 -1
- package/build/AudioAnalysis/extractAudioData.js.map +0 -1
- package/build/AudioAnalysis/extractMelSpectrogram.d.ts.map +0 -1
- package/build/AudioAnalysis/extractMelSpectrogram.js.map +0 -1
- package/build/AudioAnalysis/extractPreview.d.ts.map +0 -1
- package/build/AudioAnalysis/extractPreview.js.map +0 -1
- package/build/AudioAnalysis/extractWaveform.d.ts.map +0 -1
- package/build/AudioAnalysis/extractWaveform.js.map +0 -1
- package/build/AudioDeviceManager.d.ts.map +0 -1
- package/build/AudioDeviceManager.js.map +0 -1
- package/build/AudioRecorder.provider.d.ts.map +0 -1
- package/build/AudioRecorder.provider.js.map +0 -1
- package/build/ExpoAudioStream.native.d.ts.map +0 -1
- package/build/ExpoAudioStream.native.js.map +0 -1
- package/build/ExpoAudioStream.types.d.ts.map +0 -1
- package/build/ExpoAudioStream.types.js.map +0 -1
- package/build/ExpoAudioStream.web.d.ts.map +0 -1
- package/build/ExpoAudioStream.web.js.map +0 -1
- package/build/ExpoAudioStreamModule.d.ts.map +0 -1
- package/build/ExpoAudioStreamModule.js.map +0 -1
- package/build/WebRecorder.web.d.ts.map +0 -1
- package/build/WebRecorder.web.js.map +0 -1
- package/build/constants.d.ts.map +0 -1
- package/build/constants.js.map +0 -1
- package/build/events.d.ts.map +0 -1
- package/build/events.js.map +0 -1
- package/build/hooks/useAudioDevices.d.ts.map +0 -1
- package/build/hooks/useAudioDevices.js.map +0 -1
- package/build/index.d.ts.map +0 -1
- package/build/index.js.map +0 -1
- package/build/trimAudio.d.ts.map +0 -1
- package/build/trimAudio.js.map +0 -1
- package/build/useAudioRecorder.d.ts.map +0 -1
- package/build/useAudioRecorder.js.map +0 -1
- package/build/utils/BlobFix.d.ts.map +0 -1
- package/build/utils/BlobFix.js.map +0 -1
- package/build/utils/audioProcessing.d.ts.map +0 -1
- package/build/utils/audioProcessing.js.map +0 -1
- package/build/utils/concatenateBuffers.d.ts.map +0 -1
- package/build/utils/concatenateBuffers.js.map +0 -1
- package/build/utils/convertPCMToFloat32.d.ts.map +0 -1
- package/build/utils/convertPCMToFloat32.js.map +0 -1
- package/build/utils/crc32.d.ts.map +0 -1
- package/build/utils/crc32.js.map +0 -1
- package/build/utils/encodingToBitDepth.d.ts.map +0 -1
- package/build/utils/encodingToBitDepth.js.map +0 -1
- package/build/utils/getWavFileInfo.d.ts.map +0 -1
- package/build/utils/getWavFileInfo.js.map +0 -1
- package/build/utils/writeWavHeader.d.ts.map +0 -1
- package/build/utils/writeWavHeader.js.map +0 -1
- package/build/workers/InlineFeaturesExtractor.web.d.ts.map +0 -1
- package/build/workers/InlineFeaturesExtractor.web.js.map +0 -1
- package/build/workers/inlineAudioWebWorker.web.d.ts.map +0 -1
- package/build/workers/inlineAudioWebWorker.web.js.map +0 -1
- /package/build/{AudioAnalysis → esm/AudioAnalysis}/AudioAnalysis.types.js +0 -0
- /package/build/{AudioAnalysis → esm/AudioAnalysis}/extractAudioAnalysis.js +0 -0
- /package/build/{AudioAnalysis → esm/AudioAnalysis}/extractAudioData.js +0 -0
- /package/build/{AudioAnalysis → esm/AudioAnalysis}/extractMelSpectrogram.js +0 -0
- /package/build/{AudioAnalysis → esm/AudioAnalysis}/extractPreview.js +0 -0
- /package/build/{AudioAnalysis → esm/AudioAnalysis}/extractWaveform.js +0 -0
- /package/build/{AudioDeviceManager.js → esm/AudioDeviceManager.js} +0 -0
- /package/build/{AudioRecorder.provider.js → esm/AudioRecorder.provider.js} +0 -0
- /package/build/{ExpoAudioStream.native.js → esm/ExpoAudioStream.native.js} +0 -0
- /package/build/{ExpoAudioStream.types.js → esm/ExpoAudioStream.types.js} +0 -0
- /package/build/{ExpoAudioStream.web.js → esm/ExpoAudioStream.web.js} +0 -0
- /package/build/{ExpoAudioStreamModule.js → esm/ExpoAudioStreamModule.js} +0 -0
- /package/build/{WebRecorder.web.js → esm/WebRecorder.web.js} +0 -0
- /package/build/{constants.js → esm/constants.js} +0 -0
- /package/build/{events.js → esm/events.js} +0 -0
- /package/build/{hooks → esm/hooks}/useAudioDevices.js +0 -0
- /package/build/{index.js → esm/index.js} +0 -0
- /package/build/{trimAudio.js → esm/trimAudio.js} +0 -0
- /package/build/{useAudioRecorder.js → esm/useAudioRecorder.js} +0 -0
- /package/build/{utils → esm/utils}/BlobFix.js +0 -0
- /package/build/{utils → esm/utils}/audioProcessing.js +0 -0
- /package/build/{utils → esm/utils}/concatenateBuffers.js +0 -0
- /package/build/{utils → esm/utils}/convertPCMToFloat32.js +0 -0
- /package/build/{utils → esm/utils}/crc32.js +0 -0
- /package/build/{utils → esm/utils}/encodingToBitDepth.js +0 -0
- /package/build/{utils → esm/utils}/getWavFileInfo.js +0 -0
- /package/build/{utils → esm/utils}/writeWavHeader.js +0 -0
- /package/build/{workers → esm/workers}/InlineFeaturesExtractor.web.js +0 -0
- /package/build/{workers → esm/workers}/inlineAudioWebWorker.web.js +0 -0
- /package/build/{AudioAnalysis → types/AudioAnalysis}/AudioAnalysis.types.d.ts +0 -0
- /package/build/{AudioAnalysis → types/AudioAnalysis}/extractAudioAnalysis.d.ts +0 -0
- /package/build/{AudioAnalysis → types/AudioAnalysis}/extractAudioData.d.ts +0 -0
- /package/build/{AudioAnalysis → types/AudioAnalysis}/extractMelSpectrogram.d.ts +0 -0
- /package/build/{AudioAnalysis → types/AudioAnalysis}/extractPreview.d.ts +0 -0
- /package/build/{AudioAnalysis → types/AudioAnalysis}/extractWaveform.d.ts +0 -0
- /package/build/{AudioDeviceManager.d.ts → types/AudioDeviceManager.d.ts} +0 -0
- /package/build/{AudioRecorder.provider.d.ts → types/AudioRecorder.provider.d.ts} +0 -0
- /package/build/{ExpoAudioStream.native.d.ts → types/ExpoAudioStream.native.d.ts} +0 -0
- /package/build/{ExpoAudioStream.types.d.ts → types/ExpoAudioStream.types.d.ts} +0 -0
- /package/build/{ExpoAudioStream.web.d.ts → types/ExpoAudioStream.web.d.ts} +0 -0
- /package/build/{ExpoAudioStreamModule.d.ts → types/ExpoAudioStreamModule.d.ts} +0 -0
- /package/build/{WebRecorder.web.d.ts → types/WebRecorder.web.d.ts} +0 -0
- /package/build/{constants.d.ts → types/constants.d.ts} +0 -0
- /package/build/{events.d.ts → types/events.d.ts} +0 -0
- /package/build/{hooks → types/hooks}/useAudioDevices.d.ts +0 -0
- /package/build/{index.d.ts → types/index.d.ts} +0 -0
- /package/build/{trimAudio.d.ts → types/trimAudio.d.ts} +0 -0
- /package/build/{useAudioRecorder.d.ts → types/useAudioRecorder.d.ts} +0 -0
- /package/build/{utils → types/utils}/BlobFix.d.ts +0 -0
- /package/build/{utils → types/utils}/audioProcessing.d.ts +0 -0
- /package/build/{utils → types/utils}/concatenateBuffers.d.ts +0 -0
- /package/build/{utils → types/utils}/convertPCMToFloat32.d.ts +0 -0
- /package/build/{utils → types/utils}/crc32.d.ts +0 -0
- /package/build/{utils → types/utils}/encodingToBitDepth.d.ts +0 -0
- /package/build/{utils → types/utils}/getWavFileInfo.d.ts +0 -0
- /package/build/{utils → types/utils}/writeWavHeader.d.ts +0 -0
- /package/build/{workers → types/workers}/InlineFeaturesExtractor.web.d.ts +0 -0
- /package/build/{workers → types/workers}/inlineAudioWebWorker.web.d.ts +0 -0
|
@@ -37,43 +37,66 @@ class AudioNotificationManager {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
func showInitialNotification() {
|
|
40
|
-
|
|
40
|
+
// Wrap notification generation in a main thread dispatch
|
|
41
|
+
DispatchQueue.main.async { [weak self] in
|
|
42
|
+
guard let self = self else { return }
|
|
43
|
+
|
|
44
|
+
// No need for try-catch as this method doesn't throw
|
|
45
|
+
self.updateNotification()
|
|
46
|
+
}
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
func startUpdates(startTime: Date) {
|
|
44
|
-
|
|
45
|
-
|
|
50
|
+
// Cancel any existing timer first
|
|
51
|
+
stopUpdates()
|
|
52
|
+
|
|
53
|
+
// Create a new timer on the main thread
|
|
54
|
+
DispatchQueue.main.async { [weak self] in
|
|
55
|
+
guard let self = self else { return }
|
|
46
56
|
|
|
47
57
|
self.updateTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
|
|
48
58
|
guard let self = self else { return }
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if now.timeIntervalSince(self.lastUpdateTime) >= self.minUpdateInterval {
|
|
52
|
-
self.updateNotification()
|
|
53
|
-
self.lastUpdateTime = now
|
|
54
|
-
}
|
|
59
|
+
self.currentDuration = Date().timeIntervalSince(startTime)
|
|
60
|
+
self.updateState(isPaused: false)
|
|
55
61
|
}
|
|
56
|
-
RunLoop.main.add(self.updateTimer!, forMode: .common)
|
|
57
62
|
|
|
58
|
-
|
|
63
|
+
// Run the timer even when scrolling
|
|
64
|
+
self.updateTimer?.tolerance = 0.1
|
|
65
|
+
RunLoop.current.add(self.updateTimer!, forMode: .common)
|
|
66
|
+
|
|
67
|
+
// Update notification immediately
|
|
68
|
+
self.updateState(isPaused: false)
|
|
59
69
|
}
|
|
60
70
|
}
|
|
61
71
|
|
|
62
72
|
func stopUpdates() {
|
|
63
|
-
|
|
73
|
+
// Always execute timer invalidation on main thread
|
|
74
|
+
DispatchQueue.main.async { [weak self] in
|
|
75
|
+
guard let self = self else { return }
|
|
76
|
+
|
|
64
77
|
self.updateTimer?.invalidate()
|
|
65
78
|
self.updateTimer = nil
|
|
66
79
|
|
|
67
|
-
|
|
68
|
-
|
|
80
|
+
// Clean up notification
|
|
81
|
+
do {
|
|
82
|
+
self.notificationCenter.removeDeliveredNotifications(withIdentifiers: [self.notificationId])
|
|
83
|
+
self.notificationCenter.removePendingNotificationRequests(withIdentifiers: [self.notificationId])
|
|
84
|
+
} catch {
|
|
85
|
+
Logger.debug("AudioNotificationManager", "Error removing notifications: \(error)")
|
|
86
|
+
}
|
|
69
87
|
}
|
|
70
88
|
}
|
|
71
89
|
|
|
72
90
|
func updateState(isPaused: Bool) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
91
|
+
DispatchQueue.main.async { [weak self] in
|
|
92
|
+
guard let self = self else { return }
|
|
93
|
+
|
|
94
|
+
let now = Date()
|
|
95
|
+
if now.timeIntervalSince(self.lastUpdateTime) >= self.minUpdateInterval {
|
|
96
|
+
// No need for try-catch as this method doesn't throw
|
|
97
|
+
self.updateNotification(forcePauseState: isPaused)
|
|
98
|
+
self.lastUpdateTime = now
|
|
99
|
+
}
|
|
77
100
|
}
|
|
78
101
|
}
|
|
79
102
|
|
|
@@ -92,7 +115,7 @@ class AudioNotificationManager {
|
|
|
92
115
|
guard let self = self else { return }
|
|
93
116
|
|
|
94
117
|
// If we have a notification and it was recently updated, skip
|
|
95
|
-
if let
|
|
118
|
+
if let _ = notifications.first(where: { $0.request.identifier == self.notificationId }),
|
|
96
119
|
Date().timeIntervalSince(self.lastUpdateTime) < self.minUpdateInterval {
|
|
97
120
|
return
|
|
98
121
|
}
|
|
@@ -337,7 +337,6 @@ func computeMelSpectrogram(from segment: [Float], sampleRate: Float) -> [Float]
|
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
func computeSpectralContrast(from segment: [Float], sampleRate: Float) -> [Float] {
|
|
340
|
-
let nBands = 7
|
|
341
340
|
let fftData = sharedFFT.processSegment(segment)
|
|
342
341
|
|
|
343
342
|
let magnitudeSpectrum = computeMagnitudeSpectrum(from: fftData)
|
|
@@ -425,7 +424,7 @@ func loadAudioFile(_ fileUri: String) throws -> AudioData {
|
|
|
425
424
|
let frameCount = UInt32(file.length)
|
|
426
425
|
let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCount)!
|
|
427
426
|
|
|
428
|
-
try file.read(into: buffer)
|
|
427
|
+
try file.read(into: buffer, frameCount: frameCount)
|
|
429
428
|
|
|
430
429
|
// Convert buffer to float array
|
|
431
430
|
let samples: [Float]
|
|
@@ -490,8 +489,8 @@ func computeFeatures(segmentData: [Float], sampleRate: Float, sumSquares: Float,
|
|
|
490
489
|
let zcr = featureOptions["zcr"] == true ? Float(zeroCrossings) / Float(segmentLength) : 0
|
|
491
490
|
|
|
492
491
|
// Compute min and max amplitudes
|
|
493
|
-
let
|
|
494
|
-
let
|
|
492
|
+
let _ = segmentData.min() ?? 0
|
|
493
|
+
let _ = segmentData.max() ?? 0
|
|
495
494
|
|
|
496
495
|
// Call feature extraction functions
|
|
497
496
|
let mfcc = featureOptions["mfcc"] == true ? extractMFCC(from: segmentData, sampleRate: sampleRate) : []
|
|
@@ -598,12 +597,13 @@ func extractRawAudioData(
|
|
|
598
597
|
finalBuffer = AVAudioPCMBuffer(pcmFormat: targetFormat, frameCapacity: frameCount)!
|
|
599
598
|
|
|
600
599
|
var error: NSError?
|
|
601
|
-
|
|
600
|
+
_ = converter.convert(to: finalBuffer, error: &error) { inNumPackets, outStatus in
|
|
602
601
|
outStatus.pointee = .haveData
|
|
603
602
|
return buffer
|
|
604
603
|
}
|
|
605
604
|
|
|
606
605
|
if let error = error {
|
|
606
|
+
Logger.debug("AudioProcessingHelpers", "Format conversion failed: \(error.localizedDescription)")
|
|
607
607
|
throw error
|
|
608
608
|
}
|
|
609
609
|
} else {
|
package/ios/AudioProcessor.swift
CHANGED
|
@@ -142,7 +142,7 @@ public class AudioProcessor {
|
|
|
142
142
|
|
|
143
143
|
let totalFrameCount = AVAudioFrameCount(audioFile.length)
|
|
144
144
|
var framesPerBuffer: AVAudioFrameCount
|
|
145
|
-
let
|
|
145
|
+
let _: Int // Changed from actualPointsPerSecond
|
|
146
146
|
|
|
147
147
|
NSLog("""
|
|
148
148
|
[AudioProcessor] Starting audio processing:
|
|
@@ -215,7 +215,7 @@ public class AudioProcessor {
|
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
channelCount = Int(audioFile.processingFormat.channelCount)
|
|
218
|
-
|
|
218
|
+
let _ = Array(repeating: [Float](repeating: 0, count: Int(framesPerBuffer)), count: channelCount) // Changed from var data
|
|
219
219
|
|
|
220
220
|
var channelData = [Float]()
|
|
221
221
|
while startFrame < endFrame {
|
|
@@ -517,7 +517,7 @@ public class AudioProcessor {
|
|
|
517
517
|
|
|
518
518
|
let startTime = CACurrentMediaTime()
|
|
519
519
|
let sampleRate = Float(audioFile.fileFormat.sampleRate)
|
|
520
|
-
let
|
|
520
|
+
let _ = AVAudioFrameCount(audioFile.length) // Changed from totalFrameCount
|
|
521
521
|
let bitDepth = audioFile.fileFormat.settings[AVLinearPCMBitDepthKey] as? Int ?? 16
|
|
522
522
|
let numberOfChannels = Int(audioFile.fileFormat.channelCount)
|
|
523
523
|
|
|
@@ -595,7 +595,7 @@ public class AudioProcessor {
|
|
|
595
595
|
dB: Float(20 * log10(Double(rms))), // Use RMS for dB calculation
|
|
596
596
|
silent: rms < SILENCE_THRESHOLD_RMS, // Use RMS for silence detection
|
|
597
597
|
features: computeFeatures(
|
|
598
|
-
segmentData: Array(
|
|
598
|
+
segmentData: Array(summedData[0..<Int(framesToRead)]), // Fixed dangling pointer
|
|
599
599
|
sampleRate: sampleRate,
|
|
600
600
|
sumSquares: rms * rms,
|
|
601
601
|
zeroCrossings: 0,
|
|
@@ -692,7 +692,7 @@ public class AudioProcessor {
|
|
|
692
692
|
|
|
693
693
|
// Output format setup
|
|
694
694
|
let requestedFormat = outputFormat?["format"] as? String ?? "wav"
|
|
695
|
-
let validFormats = ["wav", "aac"
|
|
695
|
+
let validFormats = ["wav", "aac"]
|
|
696
696
|
let formatStr = validFormats.contains(requestedFormat.lowercased()) ? requestedFormat.lowercased() : "aac"
|
|
697
697
|
|
|
698
698
|
if formatStr != requestedFormat.lowercased() {
|
|
@@ -704,7 +704,7 @@ public class AudioProcessor {
|
|
|
704
704
|
let targetBitDepth = outputFormat?["bitDepth"] as? Int ?? 16
|
|
705
705
|
let bitrate = outputFormat?["bitrate"] as? Int ?? 128000
|
|
706
706
|
|
|
707
|
-
let fileExtension = formatStr == "wav" ? "wav" :
|
|
707
|
+
let fileExtension = formatStr == "wav" ? "wav" : "aac"
|
|
708
708
|
let outputURL = FileManager.default.temporaryDirectory
|
|
709
709
|
.appendingPathComponent(outputFileName ?? UUID().uuidString)
|
|
710
710
|
.appendingPathExtension(fileExtension)
|
|
@@ -753,7 +753,7 @@ public class AudioProcessor {
|
|
|
753
753
|
Logger.debug("AudioProcessor", "Trim operation completed")
|
|
754
754
|
Logger.debug("AudioProcessor", "- Output file: \(outputURL.path)")
|
|
755
755
|
Logger.debug("AudioProcessor", "- File exists: \(FileManager.default.fileExists(atPath: outputURL.path))")
|
|
756
|
-
Logger.debug("AudioProcessor", "- File size: \(try? FileManager.default.attributesOfItem(atPath: outputURL.path)[.size] as? Int64 ?? 0) bytes")
|
|
756
|
+
Logger.debug("AudioProcessor", "- File size: \((try? FileManager.default.attributesOfItem(atPath: outputURL.path)[.size] as? Int64) ?? 0) bytes") // Fixed optional unwrapping
|
|
757
757
|
Logger.debug("AudioProcessor", "- File extension: \(outputURL.pathExtension)")
|
|
758
758
|
|
|
759
759
|
return createTrimResult(from: outputURL, keepRanges: keepRanges, formatStr: formatStr, sampleRate: Int(inputSampleRate), channels: inputChannels, bitDepth: 16, bitrate: bitrate)
|
|
@@ -808,8 +808,8 @@ public class AudioProcessor {
|
|
|
808
808
|
}
|
|
809
809
|
if let error = error {
|
|
810
810
|
Logger.debug("AudioProcessor", "Format conversion failed: \(error.localizedDescription)")
|
|
811
|
-
|
|
812
|
-
|
|
811
|
+
Logger.debug("AudioProcessor", "Skipping this buffer")
|
|
812
|
+
continue
|
|
813
813
|
}
|
|
814
814
|
try outputFile.write(from: convertedBuffer)
|
|
815
815
|
cumulativeFrames += Int64(frameCount)
|
|
@@ -818,227 +818,53 @@ public class AudioProcessor {
|
|
|
818
818
|
}
|
|
819
819
|
return createTrimResult(from: outputURL, keepRanges: keepRanges, formatStr: formatStr, sampleRate: Int(targetSampleRate), channels: targetChannels, bitDepth: targetBitDepth, bitrate: bitrate)
|
|
820
820
|
} else {
|
|
821
|
-
// AAC
|
|
822
|
-
|
|
823
|
-
let fileType: AVFileType
|
|
821
|
+
// Use AAC instead of Opus (Opus support removed)
|
|
822
|
+
Logger.debug("AudioProcessor", "Using AAC format instead of requested \(formatStr)")
|
|
824
823
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
let supportedSampleRates = [8000.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0, 44100.0, 48000.0]
|
|
835
|
-
|
|
836
|
-
// Default to 44100 if not specified
|
|
837
|
-
var sampleRate = outputFormat?["sampleRate"] as? Double ?? 44100.0
|
|
838
|
-
|
|
839
|
-
// Find closest supported sample rate
|
|
840
|
-
if !supportedSampleRates.contains(sampleRate) {
|
|
841
|
-
let closestRate = supportedSampleRates.min(by: { abs($0 - sampleRate) < abs($1 - sampleRate) }) ?? 44100.0
|
|
842
|
-
Logger.debug("AudioProcessor", "Unsupported sample rate \(sampleRate)Hz for AAC, using closest supported rate: \(closestRate)Hz")
|
|
843
|
-
sampleRate = closestRate
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// Validate channels (AAC typically supports 1 or 2 channels)
|
|
847
|
-
var channels = outputFormat?["channels"] as? Int ?? 2
|
|
848
|
-
if channels > 2 {
|
|
849
|
-
Logger.debug("AudioProcessor", "AAC encoding doesn't support \(channels) channels, limiting to 2 channels")
|
|
850
|
-
channels = 2
|
|
851
|
-
} else if channels < 1 {
|
|
852
|
-
channels = 1
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
// Validate bitrate (AAC typically supports 8000-320000 bps)
|
|
856
|
-
var bitrate = outputFormat?["bitrate"] as? Int ?? 128000
|
|
857
|
-
if bitrate < 8000 {
|
|
858
|
-
Logger.debug("AudioProcessor", "AAC bitrate too low, setting to minimum 8000 bps")
|
|
859
|
-
bitrate = 8000
|
|
860
|
-
} else if bitrate > 320000 {
|
|
861
|
-
Logger.debug("AudioProcessor", "AAC bitrate too high, setting to maximum 320000 bps")
|
|
862
|
-
bitrate = 320000
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
// Set up proper audio settings for AAC
|
|
866
|
-
outputSettings = [
|
|
867
|
-
AVFormatIDKey: kAudioFormatMPEG4AAC,
|
|
868
|
-
AVSampleRateKey: sampleRate,
|
|
869
|
-
AVNumberOfChannelsKey: channels,
|
|
870
|
-
AVEncoderBitRateKey: bitrate,
|
|
871
|
-
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
|
|
872
|
-
]
|
|
873
|
-
fileType = .m4a
|
|
874
|
-
|
|
875
|
-
Logger.debug("AudioProcessor", """
|
|
876
|
-
Configuring AAC output:
|
|
877
|
-
- Container: m4a
|
|
878
|
-
- Format: AAC
|
|
879
|
-
- Sample rate: \(sampleRate)Hz
|
|
880
|
-
- Channels: \(channels)
|
|
881
|
-
- Bitrate: \(bitrate) bps
|
|
882
|
-
- Output path: \(tempOutputURL.path)
|
|
883
|
-
- File type: \(fileType)
|
|
884
|
-
""")
|
|
885
|
-
} else {
|
|
886
|
-
// Opus settings - use CAF container which can hold Opus
|
|
887
|
-
outputSettings = [
|
|
888
|
-
AVFormatIDKey: kAudioFormatOpus,
|
|
889
|
-
AVSampleRateKey: targetSampleRate,
|
|
890
|
-
AVNumberOfChannelsKey: targetChannels,
|
|
891
|
-
AVEncoderBitRateKey: bitrate
|
|
892
|
-
]
|
|
893
|
-
fileType = .caf // Core Audio Format can contain Opus
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
// Use proper file extension for the container format
|
|
897
|
-
let tempFileExtension = formatStr == "aac" ? "m4a" : "caf"
|
|
898
|
-
let tempOutputURL = FileManager.default.temporaryDirectory
|
|
899
|
-
.appendingPathComponent(outputFileName ?? UUID().uuidString)
|
|
900
|
-
.appendingPathExtension(tempFileExtension)
|
|
901
|
-
|
|
902
|
-
// Create the asset writer with the appropriate file type
|
|
903
|
-
let assetWriter = try AVAssetWriter(
|
|
904
|
-
outputURL: tempOutputURL,
|
|
905
|
-
fileType: fileType
|
|
906
|
-
)
|
|
824
|
+
// Keep the existing AAC settings structure for consistency
|
|
825
|
+
let outputSettings: [String: Any] = [
|
|
826
|
+
AVFormatIDKey: kAudioFormatMPEG4AAC,
|
|
827
|
+
AVSampleRateKey: targetSampleRate,
|
|
828
|
+
AVNumberOfChannelsKey: targetChannels,
|
|
829
|
+
AVEncoderBitRateKey: bitrate,
|
|
830
|
+
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
|
|
831
|
+
]
|
|
832
|
+
let _ = AVFileType.m4a // Changed from fileType
|
|
907
833
|
|
|
908
|
-
//
|
|
909
|
-
let
|
|
910
|
-
writerInput.expectsMediaDataInRealTime = false
|
|
911
|
-
assetWriter.add(writerInput)
|
|
834
|
+
// 4. Update container extension logic for when Opus was selected
|
|
835
|
+
let _ = "m4a" // Changed from tempFileExtension
|
|
912
836
|
|
|
913
|
-
//
|
|
914
|
-
|
|
915
|
-
assetWriter.startSession(atSourceTime: CMTime.zero)
|
|
837
|
+
// 5. Update the MIME type logic for AAC only
|
|
838
|
+
let _ = "audio/mp4" // Changed from mimeType
|
|
916
839
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
let pcmBuffer = AVAudioPCMBuffer(pcmFormat: targetFormat, frameCapacity: AVAudioFrameCount(bufferSize))!
|
|
920
|
-
|
|
840
|
+
let outputFile = try AVAudioFile(forWriting: outputURL, settings: outputSettings)
|
|
841
|
+
var totalFrames: Int64 = 0
|
|
921
842
|
for range in keepRanges {
|
|
843
|
+
// Break down complex expressions
|
|
922
844
|
let startTimeInSeconds = range[0] / 1000
|
|
923
845
|
let startFrame = AVAudioFramePosition(startTimeInSeconds * inputSampleRate)
|
|
924
846
|
|
|
925
847
|
let endTimeInSeconds = range[1] / 1000
|
|
926
848
|
let endFramePosition = endTimeInSeconds * inputSampleRate
|
|
927
|
-
let
|
|
849
|
+
let frameCount = AVAudioFrameCount(endFramePosition - Double(startFrame))
|
|
928
850
|
|
|
929
|
-
|
|
930
|
-
var framesProcessed: AVAudioFrameCount = 0
|
|
851
|
+
let buffer = AVAudioPCMBuffer(pcmFormat: inputFormat, frameCapacity: frameCount)!
|
|
931
852
|
audioFile.framePosition = startFrame
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
do {
|
|
938
|
-
try audioFile.read(into: buffer, frameCount: framesToRead)
|
|
939
|
-
|
|
940
|
-
// Convert the buffer to the target format
|
|
941
|
-
let converter = AVAudioConverter(from: inputFormat, to: targetFormat)!
|
|
942
|
-
let convertedBuffer = AVAudioPCMBuffer(pcmFormat: targetFormat, frameCapacity: framesToRead)!
|
|
943
|
-
|
|
944
|
-
var error: NSError?
|
|
945
|
-
_ = converter.convert(to: convertedBuffer, error: &error) { inNumPackets, outStatus in
|
|
946
|
-
outStatus.pointee = .haveData
|
|
947
|
-
return buffer
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
if let error = error {
|
|
951
|
-
Logger.debug("AudioProcessor", "Conversion error: \(error)")
|
|
952
|
-
continue
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
// Create a sample buffer and append to writer
|
|
956
|
-
if let sampleBuffer = createSampleBuffer(from: convertedBuffer) {
|
|
957
|
-
// Wait until the writer is ready
|
|
958
|
-
while !writerInput.isReadyForMoreMediaData {
|
|
959
|
-
Thread.sleep(forTimeInterval: 0.01)
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
if !writerInput.append(sampleBuffer) {
|
|
963
|
-
Logger.debug("AudioProcessor", "Failed to append sample buffer: \(assetWriter.error?.localizedDescription ?? "Unknown error")")
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
framesProcessed += framesToRead
|
|
968
|
-
cumulativeFrames += Int64(framesToRead)
|
|
969
|
-
let progress = Float(cumulativeFrames) / Float(totalFrames) * 100
|
|
970
|
-
progressCallback?(progress, 0, totalFrames * Int64(inputFormat.streamDescription.pointee.mBytesPerFrame))
|
|
971
|
-
|
|
972
|
-
if framesProcessed % 10000 == 0 { // Log every 10000 frames to avoid excessive logging
|
|
973
|
-
Logger.debug("AudioProcessor", "Processed \(framesProcessed)/\(totalFramesToProcess) frames")
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
} catch {
|
|
977
|
-
Logger.debug("AudioProcessor", "Error reading audio: \(error)")
|
|
978
|
-
break
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
// Finish writing properly
|
|
984
|
-
writerInput.markAsFinished()
|
|
985
|
-
let finishSemaphore = DispatchSemaphore(value: 0)
|
|
986
|
-
assetWriter.finishWriting {
|
|
987
|
-
if let error = assetWriter.error {
|
|
988
|
-
Logger.debug("AudioProcessor", "Error finishing writing: \(error)")
|
|
989
|
-
} else {
|
|
990
|
-
Logger.debug("AudioProcessor", "Writing finished successfully")
|
|
991
|
-
|
|
992
|
-
// Verify the output file
|
|
993
|
-
let fileExists = FileManager.default.fileExists(atPath: tempOutputURL.path)
|
|
994
|
-
let fileSize = (try? FileManager.default.attributesOfItem(atPath: tempOutputURL.path)[.size] as? Int64) ?? 0
|
|
995
|
-
|
|
996
|
-
Logger.debug("AudioProcessor", """
|
|
997
|
-
Output file verification:
|
|
998
|
-
- Path: \(tempOutputURL.path)
|
|
999
|
-
- Exists: \(fileExists)
|
|
1000
|
-
- Size: \(fileSize) bytes
|
|
1001
|
-
- Extension: \(tempOutputURL.pathExtension)
|
|
1002
|
-
""")
|
|
1003
|
-
}
|
|
1004
|
-
finishSemaphore.signal()
|
|
1005
|
-
}
|
|
1006
|
-
finishSemaphore.wait()
|
|
1007
|
-
|
|
1008
|
-
// Verify the file was created successfully
|
|
1009
|
-
guard FileManager.default.fileExists(atPath: tempOutputURL.path) else {
|
|
1010
|
-
reject("FILE_CREATION_FAILED", "Failed to create output file")
|
|
1011
|
-
return nil
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
// Create compression info
|
|
1015
|
-
var compressionInfo: [String: Any] = [
|
|
1016
|
-
"format": formatStr,
|
|
1017
|
-
"bitrate": bitrate,
|
|
1018
|
-
"size": (try? FileManager.default.attributesOfItem(atPath: tempOutputURL.path)[.size] as? Int64) ?? 0
|
|
1019
|
-
]
|
|
1020
|
-
|
|
1021
|
-
// Add fallback information if applicable
|
|
1022
|
-
if formatStr != requestedFormat.lowercased() {
|
|
1023
|
-
compressionInfo["requestedFormat"] = requestedFormat
|
|
1024
|
-
compressionInfo["fallbackReason"] = "Unsupported format"
|
|
853
|
+
try audioFile.read(into: buffer, frameCount: frameCount)
|
|
854
|
+
try outputFile.write(from: buffer)
|
|
855
|
+
totalFrames += Int64(frameCount)
|
|
856
|
+
let progress = Float(cumulativeFrames) / Float(totalFrames) * 100
|
|
857
|
+
progressCallback?(progress, 0, totalFrames * Int64(inputFormat.streamDescription.pointee.mBytesPerFrame))
|
|
1025
858
|
}
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
sampleRate: Int(targetSampleRate),
|
|
1036
|
-
channels: targetChannels,
|
|
1037
|
-
bitDepth: 16,
|
|
1038
|
-
mimeType: mimeType,
|
|
1039
|
-
requestedFormat: formatStr,
|
|
1040
|
-
actualFormat: tempFileExtension,
|
|
1041
|
-
compression: compressionInfo
|
|
859
|
+
return createTrimResult(
|
|
860
|
+
from: outputURL,
|
|
861
|
+
keepRanges: keepRanges,
|
|
862
|
+
formatStr: formatStr,
|
|
863
|
+
sampleRate: Int(targetSampleRate),
|
|
864
|
+
channels: targetChannels,
|
|
865
|
+
bitDepth: 16,
|
|
866
|
+
bitrate: bitrate,
|
|
867
|
+
compression: nil
|
|
1042
868
|
)
|
|
1043
869
|
}
|
|
1044
870
|
}
|
|
@@ -1077,7 +903,7 @@ public class AudioProcessor {
|
|
|
1077
903
|
private func createTrimResult(from url: URL, keepRanges: [[Double]], formatStr: String, sampleRate: Int, channels: Int, bitDepth: Int, bitrate: Int, compression: [String: Any]? = nil) -> TrimResult {
|
|
1078
904
|
let durationMs = keepRanges.map { $0[1] - $0[0] }.reduce(0, +)
|
|
1079
905
|
let size = (try? FileManager.default.attributesOfItem(atPath: url.path)[.size] as? Int64 ?? 0) ?? 0
|
|
1080
|
-
let fileExtension = formatStr == "wav" ? "wav" :
|
|
906
|
+
let fileExtension = formatStr == "wav" ? "wav" : "aac"
|
|
1081
907
|
return TrimResult(
|
|
1082
908
|
uri: url.absoluteString,
|
|
1083
909
|
filename: url.lastPathComponent,
|