Skip to content

[Feature] NAudio For Volume Changed Instand Of Keyboard Hook#466

Closed
kitUIN wants to merge 13 commits intounchihugo:masterfrom
kitUIN:naudio
Closed

[Feature] NAudio For Volume Changed Instand Of Keyboard Hook#466
kitUIN wants to merge 13 commits intounchihugo:masterfrom
kitUIN:naudio

Conversation

@kitUIN
Copy link
Copy Markdown
Collaborator

@kitUIN kitUIN commented Mar 10, 2026

New Feature Support

  • Separate the keyboard hook part from the main window and make it an independent service: InputMonitorService. This service notifies the main program through events. And the new mode of NAudio is also included in this service.
    • VolumeChangedEvent: mute / volume up / volume down
    • MediaKeyPressedEvent: play/pause, next, previous, stop
    • LockKeyPressedEvent: CapsLock / NumLock / ScrollLock / Insert
  • Due to the changes in the volume of the listening system, it will support all sources that cause volume changes, such as the touchpad, headphones, etc.
  • Added setting options. The default mode is NAudio. If there are compatibility issues, the previous Keyboard Hook mode can be selected.

Related issues

@github-actions github-actions bot added MainWindow / Media Flyout Changes to MainWindow including the Media Flyout SettingsWindow Changes to SettingsWindow or settings pages not related to flyouts/widgets labels Mar 10, 2026
Copy link
Copy Markdown
Owner

@unchihugo unchihugo left a comment

Choose a reason for hiding this comment

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

Hi @kitUIN, looks great so far, thank you! By migrating to NAudio events we solve the issue of flyouts only displaying from keypress events. There's some issues I've found during testing that needs addressing before we merge this update! I've written them down as comments.

I've noticed that headphones or trackpad volume manipulation successfully get detected now, though media tracking still only works from keyboard presses, so the new code unfortunately only solves the problem partially. The new method also detects volume changes from things like manually changing volume from the volume mixer, do you think we can avoid that?

Also, are you sure we still need the legacy keyboard hooks system in place? I'd like to hear your thoughts on this!

Finally, to my understanding from reading your code, there's little to no overlap with my Volume Flyout PR (#387) as you've only changed the hook method. I assume that you have a better understanding of how to structure the code properly, so could you have a quick look into this just to make sure? Thank you!


private bool _isStarted;

private const int MEDIA_KEY_PLAY_PAUSE = 0xB3;
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Do you think we should add these constants to NativeMethods.cs or keep them here?

{
DispatchEventAsync(DispatchVolumeChanged);
}
else // 此时必然是 mediaKeyPressed 为 true
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Could you translate this comment as well?

return CallNextHookEx(_hookId, nCode, wParam, lParam);
}

private void RaiseLockKeyPressed(LockKeyType keyType, bool isToggled)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I've noticed an issue with displaying lock keys status with this new method: if you press the key quickly (like a normal tap), it almost always reports the state it was previously in, and not the new state. This might be because the throttle is set too long for the lock keys, and because (like the hook version) we're listening for key presses instead of state changes.

<System:String x:Key="MediaFlyoutExcludeVolumeDescription">Do not display the flyout when pressing volume up/down/mute</System:String>
<System:String x:Key="MediaFlyoutInputSourceTitle">Input Monitor Source</System:String>
<System:String x:Key="MediaFlyoutInputSourceDescription">Choose how FluentFlyout detects volume changes</System:String>
<System:String x:Key="MediaFlyoutInputSourceOptionNAudio">NAudio</System:String>
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I think we should label the hook option as "Keyboard Hook (Legacy)" so users don't necessarily think that both options are equally as good.

}

_nAudioRenderDevice.AudioEndpointVolume.OnVolumeNotification -= OnNVolumeNotification;
_nAudioRenderDevice.Dispose();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Sometimes throws user-unhandled exception "System.Runtime.InteropServices.InvalidComObjectException: 'COM object that has been separated from its underlying RCW cannot be used'" when disconnecting an audio device. It would probably be wise to wrap some of these methods in try/catch blocks

@kitUIN
Copy link
Copy Markdown
Collaborator Author

kitUIN commented Mar 11, 2026

Second Changed

  • Move constants to NativeMethods.cs to follow best
  • Updated legacy Chinese comments to English
  • Input Performance Optimization:
    • ⛔Removed the key-press cooldown interval.
    • Replaced WPF's System.Windows.Input.Keyboard with the Win32 GetKeyState API to eliminate message loop latency.
    • Events are now triggered instantly upon detecting a state change.
  • Implemented proper handling for InvalidComObjectException to ensure system stability during COM object lifecycle management.
  • Retained the original keyboard hook mode as a temporary fallback. Given that the NAudio migration is a breaking change, this provides a safety net; it can be fully removed after the new implementation is verified across multiple production versions.

⚠️Known Limitation (Edge Case)

  • Please note that no volume change event is triggered when the volume is already at 0% and decreased further. Consequently, the Flyout will not be displayed in this specific scenario.

About Volume Mixer

Currently, the system only listens to System Master Volume changes. Volume adjustments at the application level (via the Volume Mixer) are ignored.

About Volume Flyout PR

Finally, to my understanding from reading your code, there's little to no overlap with my Volume Flyout PR (#387) as you've only changed the hook method. I assume that you have a better understanding of how to structure the code properly, so could you have a quick look into this just to make sure? Thank you!

I checked it.Sure that only Hook Method

@unchihugo
Copy link
Copy Markdown
Owner

Please note that no volume change event is triggered when the volume is already at 0% and decreased further. Consequently, the Flyout will not be displayed in this specific scenario.

This is a quite important edge case actually, maybe we should combine both methods instead then?

Currently, the system only listens to System Master Volume changes. Volume adjustments at the application level (via the Volume Mixer) are ignored.

The native volume flyout and quick panel (Win + A) does still activate them. I wonder if there's a way to ignore those?

@kitUIN
Copy link
Copy Markdown
Collaborator Author

kitUIN commented Mar 12, 2026

This is a quite important edge case actually, maybe we should combine both methods instead then?

For this edge case, using both methods might be ok

The native volume flyout and quick panel (Win + A) does still activate them. I wonder if there's a way to ignore those?

Regrettably, at present I haven't found a way to ignore them.

Using NAudio to detect volume changes is a limited approach.
Perhaps I can explore a more flexible solution.

If the sole purpose is to support the touchpad and headphones, perhaps there are better ways to do it.

@unchihugo
Copy link
Copy Markdown
Owner

If the sole purpose is to support the touchpad and headphones, perhaps there are better ways to do it.

I've just found out about WM_APPCOMMAND, most notably the WM_MEDIA and WM_VOLUME messages. If this works, leveraging this instead of NAudio will help solve all these issues we currently have with keyboard hooks and NAudio volume change events.

Would you want to develop this or should I have a go at it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

MainWindow / Media Flyout Changes to MainWindow including the Media Flyout SettingsWindow Changes to SettingsWindow or settings pages not related to flyouts/widgets

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants