import SwiftUI // MARK: - LiveWaveformView /// A real-time audio waveform visualization rendered via SwiftUI `Canvas`. /// Uses a highly minimalist, monochrome, line-drawn style suitable for business applications. struct LiveWaveformView: View { /// Audio amplitude samples (0.0–1.0). Most recent sample is last. let samples: [Float] /// Height of the waveform view. var height: CGFloat = 80 /// Width of each bar in points. private let barWidth: CGFloat = 1.0 /// Spacing between bars in points. private let barSpacing: CGFloat = 2.0 /// Minimum bar height to ensure visibility even at zero amplitude. private let minBarHeight: CGFloat = 1.0 var body: some View { Canvas { context, size in let totalBarSlot = barWidth + barSpacing let maxBars = Int(size.width / totalBarSlot) let maxHeight = size.height let centerY = size.height / 2 // Take the most recent N samples that fit on screen let visibleSamples: [Float] if samples.count > maxBars { visibleSamples = Array(samples.suffix(maxBars)) } else { visibleSamples = samples } let barCount = visibleSamples.count // Draw center reference line let centerLinePath = Path { path in path.move(to: CGPoint(x: 0, y: centerY)) path.addLine(to: CGPoint(x: size.width, y: centerY)) } context.stroke( centerLinePath, with: .color(Color.primary.opacity(0.1)), lineWidth: 1 ) // Draw bars right-aligned (most recent on the right) for index in 0.. 1 { let normalizedX = CGFloat(index) / CGFloat(barCount - 1) if normalizedX < fadeWidth { opacity = Double(normalizedX / fadeWidth) } else { opacity = 1.0 } } else { opacity = 1.0 } context.opacity = opacity // Simple monochrome fill (Color.primary) context.fill(barPath, with: .color(Color.primary.opacity(0.85))) context.opacity = 1.0 // Reset } } .frame(height: height) .background( RoundedRectangle(cornerRadius: 8) .fill(Color.primary.opacity(0.01)) // Semi-transparent minimal background ) .businessBorder(cornerRadius: 8) // Thin outlined border } } // MARK: - Preview #Preview("Waveform — Active") { ZStack { Color.spaceBlack.ignoresSafeArea() VStack(spacing: 20) { LiveWaveformView( samples: (0..<80).map { _ in Float.random(in: 0.05...0.9) } ) .padding(.horizontal) LiveWaveformView( samples: (0..<40).map { i in Float(sin(Double(i) * 0.3) * 0.4 + 0.5) } ) .padding(.horizontal) } } }