Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ cmake_minimum_required(VERSION 3.20)

project(
AirPodsWindows
VERSION 1.1.0 # Don't forget to bump the version in `vcpkg.json` as well
VERSION 1.2.0 # Don't forget to bump the version in `vcpkg.json` as well
LANGUAGES C CXX
DESCRIPTION "AirPods desktop user experience enhancement program"
HOMEPAGE_URL "https://github.com/YimingZhanshen/AirPodsWindows"
Expand Down
2 changes: 1 addition & 1 deletion README-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
- **操作系统**: Windows 10/11
- **蓝牙**: 需要蓝牙 4.0+ 适配器
- **AirPods**: 支持所有 AirPods 型号
- **ANC 功能**: 需要安装 [MagicAAP 驱动](https://github.com/kavishdevar/librepods)(仅 AirPods Pro/Max/AirPods 4 ANC)
- **ANC 功能**: 需要安装 [MagicAAP 驱动](https://magicpods.app/magicaap/)(仅 AirPods Pro/Max/AirPods 4 ANC)

## 📦 安装

Expand Down
2 changes: 1 addition & 1 deletion README-TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
- **作業系統**:Windows 10/11
- **藍牙**:需要藍牙 4.0+ 介面卡
- **AirPods**:支援所有 AirPods 型號
- **ANC 功能**:需要 [MagicAAP 驅動程式](https://github.com/kavishdevar/librepods)(僅 AirPods Pro/Max/AirPods 4 ANC)
- **ANC 功能**:需要 [MagicAAP 驅動程式](https://magicpods.app/magicaap/)(僅 AirPods Pro/Max/AirPods 4 ANC)

## 📦 安裝

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
- **OS**: Windows 10/11
- **Bluetooth**: Requires Bluetooth 4.0+ adapter
- **AirPods**: All AirPods models supported
- **ANC Features**: Requires [MagicAAP driver](https://github.com/kavishdevar/librepods) (AirPods Pro/Max/AirPods 4 ANC only)
- **ANC Features**: Requires [MagicAAP driver](https://magicpods.app/magicaap/) (AirPods Pro/Max/AirPods 4 ANC only)

## 📦 Installation

Expand Down
35 changes: 30 additions & 5 deletions Source/Core/AirPods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,14 @@ void Manager::OnConversationalAwarenessChanged(bool enable)
}
}

void Manager::OnConversationalAwarenessVolumePercentChanged(uint8_t percent)
{
std::lock_guard<std::mutex> lock{_mutex};
// Clamp the value to valid range (10-100) matching UI slider constraints
_conversationalAwarenessVolumePercent = std::clamp(percent, uint8_t{10}, uint8_t{100});
LOG(Info, "Conversational awareness volume percent changed to {}%", _conversationalAwarenessVolumePercent);
}

void Manager::OnPersonalizedVolumeChanged(bool enable)
{
std::lock_guard<std::mutex> lock{_mutex};
Expand Down Expand Up @@ -910,6 +918,9 @@ void Manager::OnNoiseControlModeNotification(AAP::NoiseControlMode mode)
{
LOG(Info, "Noise control mode changed to: {}", Helper::ToString(mode).toStdString());

// Track the current noise control mode
_currentNoiseControlMode = mode;

// Update the cached state in the state manager if we have a current state
auto state = _stateMgr.GetCurrentState();
if (state.has_value()) {
Expand Down Expand Up @@ -968,20 +979,29 @@ void Manager::OnHeadTrackingData(AAP::HeadTrackingData data)
}

// Volume levels for conversational awareness
constexpr int kConversationalAwarenessVolumePercent = 40; // Volume when user is speaking
constexpr int kFullVolumePercent = 100; // Normal volume when not speaking
// kFullVolumePercent (100) signals to GlobalMedia::SetVolume to restore the saved pre-speaking volume
// The actual restoration logic is in GlobalMedia_win.cpp which restores to the saved volume, not literally 100%
constexpr int kFullVolumePercent = 100; // Signal value to restore to original volume

void Manager::OnSpeakingLevelChanged(AAP::SpeakingLevel level)
{
if (!_conversationalAwarenessEnabled) {
return;
}
Comment on lines 987 to 990
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _conversationalAwarenessEnabled member is accessed at line 988 without mutex protection, but is modified in OnConversationalAwarenessChanged with the mutex locked. Both methods can be called from different threads, creating a race condition.

OnSpeakingLevelChanged reads _conversationalAwarenessEnabled without holding _mutex, while OnConversationalAwarenessChanged at line 860 modifies it while holding _mutex. This violates thread safety.

Ensure _mutex is locked when accessing _conversationalAwarenessEnabled in OnSpeakingLevelChanged.

Copilot uses AI. Check for mistakes.

// Disable conversational awareness in transparency mode to avoid volume restoration bugs
// The AAP firmware in transparency mode doesn't reliably send restoration events
if (_currentNoiseControlMode.has_value() &&
_currentNoiseControlMode.value() == AAP::NoiseControlMode::Transparency) {
Comment on lines 991 to +995
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _currentNoiseControlMode member is accessed in OnSpeakingLevelChanged without mutex protection, but is modified in OnNoiseControlModeNotification with the mutex locked. Both methods can be called from different threads (AAP callbacks), creating a potential race condition.

The OnSpeakingLevelChanged method reads _currentNoiseControlMode at lines 994-995 without holding _mutex, while OnNoiseControlModeNotification at line 922 sets it while holding _mutex. This violates thread safety and could lead to undefined behavior or inconsistent reads.

Ensure _mutex is locked when accessing _currentNoiseControlMode in OnSpeakingLevelChanged.

Suggested change
// Disable conversational awareness in transparency mode to avoid volume restoration bugs
// The AAP firmware in transparency mode doesn't reliably send restoration events
if (_currentNoiseControlMode.has_value() &&
_currentNoiseControlMode.value() == AAP::NoiseControlMode::Transparency) {
// Take a thread-safe snapshot of the current noise control mode
std::optional<AAP::NoiseControlMode> currentNoiseControlModeCopy;
{
std::lock_guard<std::mutex> lock(_mutex);
currentNoiseControlModeCopy = _currentNoiseControlMode;
}
// Disable conversational awareness in transparency mode to avoid volume restoration bugs
// The AAP firmware in transparency mode doesn't reliably send restoration events
if (currentNoiseControlModeCopy.has_value() &&
currentNoiseControlModeCopy.value() == AAP::NoiseControlMode::Transparency) {

Copilot uses AI. Check for mistakes.
LOG(Info, "Conversational awareness disabled in transparency mode to avoid volume bugs");
return;
}

switch (level) {
case AAP::SpeakingLevel::StartedSpeaking_GreatlyReduce:
case AAP::SpeakingLevel::StartedSpeaking_GreatlyReduce2:
LOG(Info, "User started speaking - reducing media volume to {}%", kConversationalAwarenessVolumePercent);
Core::GlobalMedia::SetVolume(kConversationalAwarenessVolumePercent);
LOG(Info, "User started speaking - reducing media volume to {}%", _conversationalAwarenessVolumePercent);
Core::GlobalMedia::SetVolume(_conversationalAwarenessVolumePercent);
break;
Comment on lines +1003 to 1005
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _conversationalAwarenessVolumePercent member is accessed at line 1003-1004 without mutex protection, but is modified in OnConversationalAwarenessVolumePercentChanged with the mutex locked. Both methods can be called from different threads, creating a race condition.

OnSpeakingLevelChanged reads _conversationalAwarenessVolumePercent without holding _mutex, while OnConversationalAwarenessVolumePercentChanged at line 870 modifies it while holding _mutex. This violates thread safety.

Ensure _mutex is locked when accessing _conversationalAwarenessVolumePercent in OnSpeakingLevelChanged.

Suggested change
LOG(Info, "User started speaking - reducing media volume to {}%", _conversationalAwarenessVolumePercent);
Core::GlobalMedia::SetVolume(_conversationalAwarenessVolumePercent);
break;
{
int volumePercent;
{
std::lock_guard lock(_mutex);
volumePercent = _conversationalAwarenessVolumePercent;
}
LOG(Info, "User started speaking - reducing media volume to {}%", volumePercent);
Core::GlobalMedia::SetVolume(volumePercent);
break;
}

Copilot uses AI. Check for mistakes.

case AAP::SpeakingLevel::StoppedSpeaking:
Expand All @@ -992,7 +1012,12 @@ void Manager::OnSpeakingLevelChanged(AAP::SpeakingLevel level)
break;

default:
// Intermediate levels - could implement gradual volume adjustment
// Intermediate levels (0x04-0x07) - restore volume to be safe
// This ensures volume is restored even if final event is missed
if (static_cast<uint8_t>(level) >= 0x04 && static_cast<uint8_t>(level) <= 0x07) {
LOG(Info, "Intermediate speaking level detected - restoring media volume");
Core::GlobalMedia::SetVolume(kFullVolumePercent);
}
Comment on lines 1014 to +1020
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intermediate speaking level restoration (0x04-0x07) could trigger restoration even when the volume was never reduced. If conversational awareness was disabled or the feature just started, receiving an intermediate level event (0x04-0x07) would call SetVolume(100) without any prior reduction having occurred.

The check for _conversationalAwarenessEnabled at the start of OnSpeakingLevelChanged protects against this when the feature is disabled, but if the feature is enabled and an intermediate level is received before any actual reduction events, it could still trigger an unwanted restoration. Consider checking if a reduction session is active before restoring on intermediate levels.

Copilot uses AI. Check for mistakes.
break;
}
}
Expand Down
3 changes: 3 additions & 0 deletions Source/Core/AirPods.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ class Manager
void OnAutomaticEarDetectionChanged(bool enable);
void OnBoundDeviceAddressChanged(uint64_t address);
void OnConversationalAwarenessChanged(bool enable);
void OnConversationalAwarenessVolumePercentChanged(uint8_t percent);
void OnPersonalizedVolumeChanged(bool enable);
void OnLoudSoundReductionChanged(bool enable);
void OnAdaptiveTransparencyLevelChanged(uint8_t level);
Expand Down Expand Up @@ -203,9 +204,11 @@ class Manager
bool _deviceConnected{false};
bool _automaticEarDetection{false};
bool _conversationalAwarenessEnabled{false};
uint8_t _conversationalAwarenessVolumePercent{40};
bool _personalizedVolumeEnabled{false};
bool _loudSoundReductionEnabled{false};
uint8_t _adaptiveTransparencyLevel{25};
std::optional<AAP::NoiseControlMode> _currentNoiseControlMode;

// AAP Manager for L2CAP protocol communication
AAP::Manager _aapMgr;
Expand Down
6 changes: 6 additions & 0 deletions Source/Core/GlobalMedia.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,10 @@ inline int GetVolume()
{
return Controller::GetInstance().GetVolume();
}

inline void ClearVolumeReductionState()
{
Controller::GetInstance().ClearVolumeReductionState();
}

} // namespace Core::GlobalMedia
25 changes: 20 additions & 5 deletions Source/Core/GlobalMedia_win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -561,9 +561,10 @@ void Controller::SetVolume(int percent)
bool isReducing = percent < 100;
bool hasVolumeReduction = _savedVolume > 0;

// Save current volume before reducing (if this is the first reduction)
if (isReducing && !hasVolumeReduction) {
// Get actual current volume
// Save current volume before reducing
// ALWAYS re-read the actual volume to ensure we capture user's manual changes
if (isReducing && !_inReductionSession) {
// Get actual current volume from the system
int currentVolume = 100;
try {
OS::Windows::Com::UniquePtr<IMMDeviceEnumerator> deviceEnumerator;
Expand All @@ -588,18 +589,23 @@ void Controller::SetVolume(int percent)
}
} catch (...) {}
_savedVolume = currentVolume;
_inReductionSession = true;
LOG(Trace, "SetVolume: Saved current volume {}% before reduction, starting session", _savedVolume);
}

// Calculate target volume
int targetPercent;
if (percent == 100 && hasVolumeReduction) {
// Restoring - use saved volume
// Keep _savedVolume to handle duplicate restoration calls
// It will be cleared when the next reduction cycle starts
targetPercent = _savedVolume;
// Clear saved volume after restoring
_savedVolume = 0;
_inReductionSession = false;
LOG(Trace, "SetVolume: Restoring to saved volume {}%, ending session", targetPercent);
Comment on lines 598 to +604
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The saved volume state management has an edge case issue. When volume restoration occurs (line 598-604), _savedVolume is not cleared. This is intentional to handle duplicate restoration calls from AAP firmware. However, if the system volume happens to be at 0% when a reduction session starts, _savedVolume will be set to 0, and then hasVolumeReduction (line 562: _savedVolume > 0) will be false. This means:

  1. Future restoration calls (SetVolume(100)) won't restore anything because hasVolumeReduction is false
  2. The session flag will still be set, preventing new sessions from starting

This is an extremely rare edge case (volume at 0%), but it could cause the feature to get stuck. Consider using _inReductionSession as the primary indicator instead of _savedVolume > 0, or handle the 0% case explicitly.

Copilot uses AI. Check for mistakes.
} else if (isReducing && _savedVolume > 0) {
// Reducing - calculate relative to saved volume
targetPercent = (percent * _savedVolume) / 100;
LOG(Trace, "SetVolume: Reducing to {}% ({}% of saved {}%)", targetPercent, percent, _savedVolume);
} else {
// No reduction state - just set the percent
targetPercent = percent;
Expand Down Expand Up @@ -687,4 +693,13 @@ int Controller::GetVolume() const
return 100;
}
}

void Controller::ClearVolumeReductionState()
{
std::lock_guard<std::mutex> lock{_mutex};
_savedVolume = 0;
_inReductionSession = false;
LOG(Trace, "ClearVolumeReductionState: Cleared saved volume state");
}
Comment on lines +697 to +703
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ClearVolumeReductionState() function is defined and exposed in the public API but is never called anywhere in the codebase. This appears to be dead code that should either be used (e.g., when conversational awareness is disabled, when the device disconnects, or when noise control mode changes) or removed.

Copilot uses AI. Check for mistakes.

} // namespace Core::GlobalMedia
2 changes: 2 additions & 0 deletions Source/Core/GlobalMedia_win.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,12 @@ class Controller final : public Helper::Singleton<Controller>, public Details::C
void Pause() override;
void SetVolume(int percent);
int GetVolume() const;
void ClearVolumeReductionState(); // Explicitly clear saved volume state

private:
std::mutex _mutex;
std::vector<std::unique_ptr<Details::MediaProgramAbstract>> _pausedPrograms;
int _savedVolume{0}; // Saved volume before reduction; 0 means no active reduction
bool _inReductionSession{false}; // Track if we're currently in a volume reduction session
};
} // namespace Core::GlobalMedia
8 changes: 8 additions & 0 deletions Source/Core/Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ void OnApply_conversational_awareness(const Fields &newFields)
newFields.conversational_awareness);
}

void OnApply_conversational_awareness_volume_percent(const Fields &newFields)
{
LOG(Info, "OnApply_conversational_awareness_volume_percent: {}", newFields.conversational_awareness_volume_percent);

ApdApp->GetMainWindow()->GetApdMgr().OnConversationalAwarenessVolumePercentChanged(
newFields.conversational_awareness_volume_percent);
}

void OnApply_personalized_volume(const Fields &newFields)
{
LOG(Info, "OnApply_personalized_volume: {}", newFields.personalized_volume);
Expand Down
4 changes: 4 additions & 0 deletions Source/Core/Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ enum class LoadResult : uint32_t { AbiIncompatible, NoAbiField, Successful };
callback(bool, conversational_awareness, {false}, \
Impl::OnApply(&OnApply_conversational_awareness), \
Impl::Desc{QObject::tr("Automatically lowers media volume when you start speaking (AirPods Pro/Max only).")}) \
callback(uint8_t, conversational_awareness_volume_percent, {40}, \
Impl::OnApply(&OnApply_conversational_awareness_volume_percent), \
Impl::Desc{QObject::tr("Volume percentage when speaking (based on your pre-speaking volume).")}) \
callback(bool, personalized_volume, {false}, \
Impl::OnApply(&OnApply_personalized_volume), \
Impl::Desc{QObject::tr("Adjusts media volume based on your environment and listening habits.")}) \
Expand Down Expand Up @@ -227,6 +230,7 @@ void OnApply_auto_run(const Fields &newFields);
void OnApply_low_audio_latency(const Fields &newFields);
void OnApply_automatic_ear_detection(const Fields &newFields);
void OnApply_conversational_awareness(const Fields &newFields);
void OnApply_conversational_awareness_volume_percent(const Fields &newFields);
void OnApply_personalized_volume(const Fields &newFields);
void OnApply_loud_sound_reduction(const Fields &newFields);
void OnApply_adaptive_transparency_level(const Fields &newFields);
Expand Down
22 changes: 13 additions & 9 deletions Source/Gui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,22 +472,26 @@ void MainWindow::PlayAnimation()
{
_isAnimationPlaying = true;
// Stop player and reset position for clean replay
// Qt's QMediaPlayer handles these synchronously in the event loop
_mediaPlayer->stop();
_mediaPlayer->setPosition(0);
_videoWidget->show();
// Reinitialize video widget rendering surface after hide/show cycle
// This is called only on window show (not during playback loop), so performance impact is minimal
_mediaPlayer->setVideoOutput(_videoWidget);

// Ensure video widget is visible - critical for x64 builds
// The widget must be visible and have its video output set before playing
if (!_videoWidget->isVisible()) {
_videoWidget->show();
// On x64, we need to ensure the video output is properly connected after showing
_mediaPlayer->setVideoOutput(static_cast<QVideoWidget*>(nullptr));
_mediaPlayer->setVideoOutput(_videoWidget);
}

Comment on lines +477 to +486
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting the video output to nullptr and then immediately back to _videoWidget seems unnecessary. The comment suggests this is needed for x64 builds, but there's no clear explanation of why this double-set is required. This pattern is unusual and could be fragile.

If this is truly necessary for x64 rendering, consider adding a more detailed comment explaining the specific Qt/Windows x64 bug being worked around, or investigate whether there's a cleaner solution.

Suggested change
// Ensure video widget is visible - critical for x64 builds
// The widget must be visible and have its video output set before playing
if (!_videoWidget->isVisible()) {
_videoWidget->show();
// On x64, we need to ensure the video output is properly connected after showing
_mediaPlayer->setVideoOutput(static_cast<QVideoWidget*>(nullptr));
_mediaPlayer->setVideoOutput(_videoWidget);
}
// Ensure video widget is visible - critical for x64 builds.
// NOTE: On some Windows x64 configurations (Qt + DirectShow backend), if the
// QVideoWidget is shown *after* a full stop(), the first frame is never
// painted and the surface stays black/transparent unless the video output
// is temporarily cleared and rebound. Explicitly setting the output to
// nullptr forces the underlying video surface to be torn down, and
// immediately re‑attaching _videoWidget causes Qt to recreate and properly
// initialize the rendering pipeline for the newly shown widget.
//
// This sequence has been validated to fix the x64 rendering issue and
// must not be "simplified" by removing either call unless the underlying
// Qt/Windows bug is confirmed to be resolved and re‑tested on affected
// platforms.
if (!_videoWidget->isVisible()) {
_videoWidget->show();
_mediaPlayer->setVideoOutput(static_cast<QVideoWidget*>(nullptr));
_mediaPlayer->setVideoOutput(_videoWidget);
}

Copilot uses AI. Check for mistakes.
_mediaPlayer->play();
}

void MainWindow::StopAnimation()
{
// The player will go black after stopping
// I have no idea about this, so let's hide the widget here as a workaround
_videoWidget->hide();

// Stop the player but keep widget visible to avoid x64 rendering issues
// The widget will be black when stopped, but this is better than becoming
// transparent/hollow on x64 builds
Comment on lines +492 to +494
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping the widget visible when stopped means users will see a black rectangle instead of the widget being hidden. The comment acknowledges this ("The widget will be black when stopped") but doesn't explain why this is preferable to the transparency issue.

This could be confusing to users who see a black box instead of the expected animation or nothing at all. Consider whether there's a better solution that avoids both the transparency bug and the black rectangle, or add UI polish to make the stopped state less jarring (e.g., show a static image).

Copilot uses AI. Check for mistakes.
_isAnimationPlaying = false;
_mediaPlayer->stop();
}
Expand Down
20 changes: 18 additions & 2 deletions Source/Gui/SettingsWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QDialog{parent}

auto versionText =
QString{"<a href=\"%1\">v%2</a>"}
.arg("https://github.com/YimingZhanshen/AirPodsWindows/releases/tag/" CONFIG_VERSION_STRING)
.arg("https://github.com/YimingZhanshen/AirPodsWindows/releases/tag/v" CONFIG_VERSION_STRING)
.arg(CONFIG_VERSION_STRING);
#if defined APD_BUILD_GIT_HASH
versionText +=
Expand Down Expand Up @@ -162,6 +162,12 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QDialog{parent}
}
});

connect(_ui.sliderConversationalAwarenessVolume, &QSlider::valueChanged, this, [this](int value) {
if (_trigger) {
On_sliderConversationalAwarenessVolume_valueChanged(value);
}
});

connect(_ui.cbPersonalizedVolume, &QCheckBox::toggled, this, [this](bool checked) {
if (_trigger) {
On_cbPersonalizedVolume_toggled(checked);
Expand Down Expand Up @@ -320,7 +326,8 @@ void SettingsWindow::InitCreditsText()
};
static std::vector<RefInfo> refs{
// clang-format off
{ "librepods", "https://github.com/kavishdevar/librepods", "AAP protocol & MagicAAP driver" },
{ "librepods", "https://github.com/kavishdevar/librepods", "AAP protocol reverse engineering" },
{ "MagicAAP", "https://magicpods.app/magicaap/", "MagicAAP driver for ANC features" },
{ "OpenPods", "https://github.com/adolfintel/OpenPods", "AirPods BLE protocol" },
{ "AirPodsDesktop", "https://github.com/SpriteOvO/AirPodsDesktop", "Original project" }
// clang-format on
Expand Down Expand Up @@ -364,6 +371,9 @@ void SettingsWindow::Update(const Fields &fields, bool trigger)

_ui.cbConversationalAwareness->setChecked(fields.conversational_awareness);

_ui.sliderConversationalAwarenessVolume->setValue(fields.conversational_awareness_volume_percent);
_ui.lblConversationalAwarenessVolumeValue->setText(QString::number(fields.conversational_awareness_volume_percent) + "%");

_ui.cbPersonalizedVolume->setChecked(fields.personalized_volume);

_ui.cbLoudSoundReduction->setChecked(fields.loud_sound_reduction);
Expand Down Expand Up @@ -489,6 +499,12 @@ void SettingsWindow::On_cbConversationalAwareness_toggled(bool checked)
ModifiableAccess()->conversational_awareness = checked;
}

void SettingsWindow::On_sliderConversationalAwarenessVolume_valueChanged(int value)
{
_ui.lblConversationalAwarenessVolumeValue->setText(QString::number(value) + "%");
ModifiableAccess()->conversational_awareness_volume_percent = static_cast<uint8_t>(value);
}

void SettingsWindow::On_cbPersonalizedVolume_toggled(bool checked)
{
ModifiableAccess()->personalized_volume = checked;
Expand Down
1 change: 1 addition & 0 deletions Source/Gui/SettingsWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class SettingsWindow : public QDialog
void On_cbLowAudioLatency_toggled(bool checked);
void On_cbAutoEarDetection_toggled(bool checked);
void On_cbConversationalAwareness_toggled(bool checked);
void On_sliderConversationalAwarenessVolume_valueChanged(int value);
void On_cbPersonalizedVolume_toggled(bool checked);
void On_cbLoudSoundReduction_toggled(bool checked);
void On_cbNoiseControlMode_currentIndexChanged(int index);
Expand Down
Loading
Loading