Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions Quality/CMPlayerStuff.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,37 @@ class CMPlayerParser {
return stats
}

/// Detects whether Music is playing a lossy Dolby Atmos / Spatial stream
/// instead of a Lossless one, using the same CoreAudio logs we already fetch.
///
/// - Lossless playback decodes via `ACAppleLosslessDecoder` (subType `alac`).
/// - Atmos / Spatial playback bypasses lossless entirely and decodes the lossy
/// AAC asset (`ACMP4AACBaseDecoder`, `Output format: ... 48000 Hz`).
///
/// Returns `true` (Atmos), `false` (Lossless), or `nil` when the recent log
/// window contains no decode info — callers should keep the previous value in
/// that case rather than flip-flopping during steady playback.
static func detectAtmos(_ entries: [SimpleConsole]) -> Bool? {
var sawLossless = false
var sawLossyAAC = false

for entry in entries {
let message = entry.message
if message.contains("ACAppleLosslessDecoder.cpp") {
sawLossless = true
}
if message.contains("ACMP4AACBaseDecoder.cpp"),
message.contains("Output format:"),
message.contains("48000 Hz") {
sawLossyAAC = true
}
}

if sawLossless { return false } // a lossless stream is decoding -> not Atmos
if sawLossyAAC { return true } // lossy AAC @ 48 kHz, no lossless -> Atmos/Spatial
return nil // no decode info in window -> unknown
}
Comment on lines +126 to +146

static func parseCoreMediaConsoleLogs(_ entries: [SimpleConsole]) -> [CMPlayerStats] {
let kTimeDifferenceAcceptance = 5.0 // seconds
var lastDate: Date?
Expand Down
8 changes: 7 additions & 1 deletion Quality/MenuView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@ struct MenuView: View {

var body: some View {
VStack {
if outputDevices.isAtmosActive {
Text("⚠️ Dolby Atmos is on — not playing in Lossless")
.foregroundColor(.red)
Divider()
}
Comment on lines +17 to +21

ContentView()

Divider()

Button {
Expand Down
7 changes: 7 additions & 0 deletions Quality/OutputDevices.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class OutputDevices: ObservableObject {
@Published var outputDevices = [AudioDevice]()
@Published var currentSampleRate: Float64?
@Published var currentBitDepth: Int?
@Published var isAtmosActive = false // Music is playing lossy Atmos/Spatial, not Lossless
@Published var enableBitDepthDetection = Defaults.shared.userPreferBitDepthDetection

private var enableBitDepthDetectionCancellable: AnyCancellable?
Expand Down Expand Up @@ -143,6 +144,12 @@ class OutputDevices: ObservableObject {
// allStats.append(contentsOf: CMPlayerParser.parseCoreMediaConsoleLogs(coreMediaLogs))
// }

// Flag lossy Dolby Atmos / Spatial playback (no lossless stream to switch to).
// nil means no decode info in this window, so keep the previous value.
if let atmos = CMPlayerParser.detectAtmos(coreAudioLogs) {
DispatchQueue.main.async { self.isAtmosActive = atmos }
}
Comment on lines +149 to +151

// allStats.sort(by: {$0.priority > $1.priority})
print("[getAllStats] \(allStats)")
}
Expand Down