@@ -9,43 +9,52 @@ import Foundation
99final class FDWaveformAudioDataSource : FDWaveformDataSource {
1010 private let audioURL : URL
1111 private let asset : AVAsset
12- private let assetTrack : AVAssetTrack ?
13- private let sampleRate : Int
14- private let channelCount : Int
15- private let cachedTotalSamples : Int
12+ private var assetTrack : AVAssetTrack ?
13+ private var sampleRate : Int = 44100
14+ private var channelCount : Int = 1
15+ private var cachedTotalSamples : Int = 0
16+ private var isLoaded : Bool = false
1617
1718 init ( audioURL: URL ) {
1819 self . audioURL = audioURL
1920 self . asset = AVAsset ( url: audioURL)
21+ }
2022
21- // Get the audio track and its properties
22- let tracks = asset. tracks ( withMediaType: . audio)
23- self . assetTrack = tracks. first
24-
25- if let track = assetTrack,
26- let formatDescriptions = track. formatDescriptions as? [ CMAudioFormatDescription ] ,
27- let formatDesc = formatDescriptions. first,
28- let streamDesc = CMAudioFormatDescriptionGetStreamBasicDescription ( formatDesc)
29- {
30- self . sampleRate = Int ( streamDesc. pointee. mSampleRate)
31- self . channelCount = Int ( streamDesc. pointee. mChannelsPerFrame)
32-
33- // Calculate total samples from duration
34- let duration = asset. duration
35- let durationInSeconds = CMTimeGetSeconds ( duration)
36- self . cachedTotalSamples = Int ( durationInSeconds * Double( sampleRate) )
37- } else {
38- self . sampleRate = 44100
39- self . channelCount = 1
40- self . cachedTotalSamples = 0
23+ /// Loads audio track properties asynchronously. Called automatically on first access.
24+ private func loadTrackIfNeeded( ) async {
25+ guard !isLoaded else { return }
26+ isLoaded = true
27+
28+ do {
29+ let tracks = try await asset. loadTracks ( withMediaType: . audio)
30+ self . assetTrack = tracks. first
31+
32+ if let track = assetTrack,
33+ let formatDescriptions = try ? await track. load ( . formatDescriptions)
34+ as? [ CMAudioFormatDescription ] ,
35+ let formatDesc = formatDescriptions. first,
36+ let streamDesc = CMAudioFormatDescriptionGetStreamBasicDescription ( formatDesc)
37+ {
38+ self . sampleRate = Int ( streamDesc. pointee. mSampleRate)
39+ self . channelCount = Int ( streamDesc. pointee. mChannelsPerFrame)
40+
41+ // Calculate total samples from duration
42+ let duration = try await asset. load ( . duration)
43+ let durationInSeconds = CMTimeGetSeconds ( duration)
44+ self . cachedTotalSamples = Int ( durationInSeconds * Double( sampleRate) )
45+ }
46+ } catch {
47+ // Keep default values on error
4148 }
4249 }
4350
4451 func numberOfSamples( ) async -> Int {
52+ await loadTrackIfNeeded ( )
4553 return cachedTotalSamples
4654 }
4755
4856 func samples( in range: Range < Int > ) async -> [ Float ] {
57+ await loadTrackIfNeeded ( )
4958 guard !range. isEmpty, let track = assetTrack else { return [ ] }
5059
5160 guard let reader = try ? AVAssetReader ( asset: asset) else { return [ ] }
@@ -124,6 +133,7 @@ final class FDWaveformAudioDataSource: FDWaveformDataSource {
124133 }
125134
126135 func maximums( from range: Range < Int > , numberOfBins: Int ) async -> [ Float ] {
136+ await loadTrackIfNeeded ( )
127137 guard !range. isEmpty, numberOfBins > 0 , let track = assetTrack else { return [ ] }
128138
129139 guard let reader = try ? AVAssetReader ( asset: asset) else { return [ ] }
0 commit comments