Skip to content

Commit 8cc3275

Browse files
committed
Add playback animation, example
1 parent 77da5b6 commit 8cc3275

File tree

1 file changed

+64
-0
lines changed

1 file changed

+64
-0
lines changed

Example/Sources/ContentView.swift

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Copyright (c) William Entriken and the FDWaveformView contributors
33
//
44

5+
import AVFoundation
56
import FDWaveformView
67
import SwiftUI
78

@@ -120,6 +121,10 @@ struct ContentView: View {
120121
@State private var isShowingProfilingAlert = false
121122
@State private var profilingStartTime: Date?
122123
@State private var aacRenderTime: TimeInterval?
124+
@State private var audioPlayer: AVAudioPlayer?
125+
@State private var isPlaying = false
126+
@State private var playbackTimer: Timer?
127+
@State private var playbackStartTime: Date?
123128

124129
var body: some View {
125130
NavigationView {
@@ -227,6 +232,21 @@ struct ContentView: View {
227232
}
228233
}
229234
.frame(height: 200)
235+
236+
HStack {
237+
Button(action: playAudio) {
238+
Label("Play", systemImage: "play.fill")
239+
}
240+
.buttonStyle(.borderedProminent)
241+
.disabled(audioURL == nil)
242+
243+
Button(action: stopAudio) {
244+
Label("Stop", systemImage: "stop.fill")
245+
}
246+
.buttonStyle(.bordered)
247+
.disabled(!isPlaying)
248+
}
249+
.padding()
230250
}
231251
.padding()
232252
}
@@ -272,6 +292,50 @@ struct ContentView: View {
272292
profilingStartTime = nil
273293
}
274294
}
295+
296+
private func playAudio() {
297+
guard let audioURL = audioURL else { return }
298+
299+
do {
300+
audioPlayer = try AVAudioPlayer(contentsOf: audioURL)
301+
audioPlayer?.play()
302+
isPlaying = true
303+
progress = 0
304+
305+
// Start playback timer to update progress
306+
playbackStartTime = Date()
307+
playbackTimer = Timer.scheduledTimer(withTimeInterval: 0.016, repeats: true) { _ in
308+
updateProgress()
309+
}
310+
} catch {
311+
NSLog("Failed to play audio: \(error)")
312+
}
313+
}
314+
315+
private func stopAudio() {
316+
audioPlayer?.stop()
317+
isPlaying = false
318+
playbackTimer?.invalidate()
319+
playbackTimer = nil
320+
playbackStartTime = nil
321+
}
322+
323+
private func updateProgress() {
324+
guard isPlaying, let startTime = playbackStartTime, totalSamples > 0 else { return }
325+
326+
let elapsedTime = Date().timeIntervalSince(startTime)
327+
let sampleRate = 44100 // Submarine.aiff sample rate
328+
let currentSample = Int(elapsedTime * Double(sampleRate))
329+
330+
if currentSample < totalSamples {
331+
withAnimation(.linear(duration: 0.016)) {
332+
progress = currentSample
333+
}
334+
} else {
335+
// Playback finished
336+
stopAudio()
337+
}
338+
}
275339
}
276340

277341
#Preview {

0 commit comments

Comments
 (0)