From 574c193a6d8cedf940f13f58f739ebd114c1bf44 Mon Sep 17 00:00:00 2001 From: Ozan Durgut <49796358+ozan956@users.noreply.github.com> Date: Sat, 22 Nov 2025 20:12:00 +0100 Subject: [PATCH] linux: improve conversation detect logging (#302) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit media: correct AirPods conversational awareness state handling Fix incorrect detection of conversational awareness events. The previous implementation treated all non-0x01 packets as "disabled", which caused wrong behavior when the user manually enabled/disabled the feature or when voice-end events were received. Adds full decoding for packet types: - 0x01 → voice detected - 0x06/others → voice ended - 0x08 → feature disabled - 0x09 → feature enabled Signed-off-by: ozan956 --- linux/media/mediacontroller.cpp | 72 ++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/linux/media/mediacontroller.cpp b/linux/media/mediacontroller.cpp index 531efd5..078129c 100644 --- a/linux/media/mediacontroller.cpp +++ b/linux/media/mediacontroller.cpp @@ -101,40 +101,54 @@ bool MediaController::isActiveOutputDeviceAirPods() { } void MediaController::handleConversationalAwareness(const QByteArray &data) { - LOG_DEBUG("Handling conversational awareness data: " << data.toHex()); - bool lowered = data[9] == 0x01; - LOG_INFO("Conversational awareness: " << (lowered ? "enabled" : "disabled")); - - if (lowered) { - if (initialVolume == -1 && isActiveOutputDeviceAirPods()) { - QString defaultSink = m_pulseAudio->getDefaultSink(); - initialVolume = m_pulseAudio->getSinkVolume(defaultSink); - if (initialVolume == -1) { - LOG_ERROR("Failed to get initial volume"); + if (data.size() < 10) { + LOG_ERROR("Invalid conversational awareness packet"); return; - } - LOG_DEBUG("Initial volume: " << initialVolume << "%"); } - QString defaultSink = m_pulseAudio->getDefaultSink(); - int targetVolume = initialVolume * 0.20; - if (m_pulseAudio->setSinkVolume(defaultSink, targetVolume)) { - LOG_INFO("Volume lowered to 0.20 of initial which is " << targetVolume << "%"); - } else { - LOG_ERROR("Failed to lower volume"); + + uint8_t flag = (uint8_t)data[9]; + + switch (flag) { + case 0x01: + LOG_INFO("Conversational awareness event: voice detected"); + + if (initialVolume == -1 && isActiveOutputDeviceAirPods()) { + QString sink = m_pulseAudio->getDefaultSink(); + initialVolume = m_pulseAudio->getSinkVolume(sink); + LOG_DEBUG("Initial volume saved: " << initialVolume << "%"); + } + + if (initialVolume != -1) { + QString sink = m_pulseAudio->getDefaultSink(); + int target = initialVolume * 0.20; + m_pulseAudio->setSinkVolume(sink, target); + LOG_INFO("Volume lowered to " << target << "%"); + } + break; + + case 0x08: + LOG_INFO("Conversational awareness disabled"); + initialVolume = -1; + break; + + case 0x09: + LOG_INFO("Conversational awareness enabled"); + break; + + default: + LOG_INFO("Conversational awareness event: voice ended"); + + if (initialVolume != -1 && isActiveOutputDeviceAirPods()) { + QString sink = m_pulseAudio->getDefaultSink(); + m_pulseAudio->setSinkVolume(sink, initialVolume); + LOG_INFO("Volume restored to " << initialVolume << "%"); + initialVolume = -1; + } + break; } - } else { - if (initialVolume != -1 && isActiveOutputDeviceAirPods()) { - QString defaultSink = m_pulseAudio->getDefaultSink(); - if (m_pulseAudio->setSinkVolume(defaultSink, initialVolume)) { - LOG_INFO("Volume restored to " << initialVolume << "%"); - } else { - LOG_ERROR("Failed to restore volume"); - } - initialVolume = -1; - } - } } + bool MediaController::isA2dpProfileAvailable() { if (m_deviceOutputName.isEmpty()) { return false;