Widget v2 glassmorphism, lyrics, backup, crossfade fixes
Widget v2: - Glassmorphism glass panel, 40-bar waveform Canvas with SeekToIntent - CIAreaAverage color extraction + secondary color for adaptive theming - Small: inset glass. Medium/Large: flush edge-to-edge (contentMarginsDisabled) - Large: 3-item queue with real cover art thumbnails, crossfade countdown - Waveform sampled from offline vis buffer, seeded PRNG fallback Live Lyrics: - LyricsService: direct LRCLIB from iOS, no companion dependency - Embedded lyrics read via AVAsset.load(.metadata) from downloads - Karaoke word-by-word gradient fill, auto-scroll, fade edges - Tap-to-sync timing editor with +/-0.1s offset adjust - Companion API fallback only for server-side embedded tags Backup System: - .nvdbackup export/import via ZIPFoundation - UTI registered for AirDrop, passwords stripped on export - PendingOperationsQueue with retry + disk persistence Crossfade fixes: - Seek bar: reports from incoming player immediately, not at 50% - songHandoff at midpoint: art/colors/text/lyrics transition mid-fade - Scrobble fires before metadata swap (correct outgoing song) Visualizer fixes: - stopAll() zeros _audioLevels + clears offlineVisBuffer - All 5 simulation start sites gated behind realAudioAnalysis check - Bars stay flat between skips, only rise when real vis data loads Smart DJ bulk prefetch, appendingPathComponent slash fix
This commit is contained in:
parent
cea4e3868e
commit
842cb6353b
1 changed files with 12 additions and 5 deletions
|
|
@ -293,9 +293,10 @@ class AudioPlayer: NSObject, ObservableObject {
|
|||
|
||||
if isUsingOfflineVis {
|
||||
startOfflineVisSync()
|
||||
} else if !isUsingCrossfade {
|
||||
} else if !isUsingCrossfade && !VisualizerSettings.shared.realAudioAnalysis {
|
||||
startLevelSimulation()
|
||||
}
|
||||
// When realAudioAnalysis is on but vis data hasn't loaded yet: stay at 0.
|
||||
alog("Foreground: session reactivated, observers + vis timers resumed (crossfade=\(isUsingCrossfade) offlineVis=\(isUsingOfflineVis))")
|
||||
}
|
||||
|
||||
|
|
@ -475,7 +476,9 @@ class AudioPlayer: NSObject, ObservableObject {
|
|||
|
||||
updateNowPlayingInfo()
|
||||
fetchAndSetArtwork(coverArtId: song.coverArt)
|
||||
startLevelSimulation()
|
||||
if !VisualizerSettings.shared.realAudioAnalysis {
|
||||
startLevelSimulation()
|
||||
}
|
||||
|
||||
// Load offline visualizer for crossfade path too
|
||||
if VisualizerSettings.shared.realAudioAnalysis {
|
||||
|
|
@ -867,9 +870,12 @@ class AudioPlayer: NSObject, ObservableObject {
|
|||
#if os(iOS)
|
||||
if isRadioStream {
|
||||
startRadioSimulation()
|
||||
} else {
|
||||
} else if !VisualizerSettings.shared.realAudioAnalysis {
|
||||
// Simulation mode only — real analysis waits for offline vis data
|
||||
startLevelSimulation()
|
||||
}
|
||||
// When realAudioAnalysis is on: levels stay at 0 until
|
||||
// loadOfflineVisualizer → startOfflineVisSync fills them.
|
||||
#else
|
||||
startLevelSimulation()
|
||||
#endif
|
||||
|
|
@ -1090,7 +1096,7 @@ class AudioPlayer: NSObject, ObservableObject {
|
|||
guard let self = self else { return }
|
||||
self.setLevels(self.rawFFTLevels)
|
||||
}
|
||||
} else {
|
||||
} else if !VisualizerSettings.shared.realAudioAnalysis {
|
||||
DebugLogger.shared.log("resume: simulation path — levelTimer nil=\(levelTimer == nil)", category: "VisDebug")
|
||||
// Simulation path: seed internalLevels from a fresh random target
|
||||
// so the wave has height immediately rather than starting from floor.
|
||||
|
|
@ -1100,6 +1106,7 @@ class AudioPlayer: NSObject, ObservableObject {
|
|||
setLevels(internalLevels)
|
||||
if levelTimer == nil { startLevelSimulation() }
|
||||
}
|
||||
// else: realAudioAnalysis on, vis data loading — stay at 0
|
||||
pushWidgetState()
|
||||
#endif
|
||||
updateNowPlayingInfo()
|
||||
|
|
@ -1626,7 +1633,7 @@ class AudioPlayer: NSObject, ObservableObject {
|
|||
}
|
||||
} catch {
|
||||
alog("Offline vis: analysis failed: \(error.localizedDescription)")
|
||||
await MainActor.run { self.startLevelSimulation() }
|
||||
// Don't fall back to simulation — levels stay at 0
|
||||
}
|
||||
} // end if !fetched
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue