Skip to content

Conversation

@hatzibod
Copy link
Contributor

Add per-user volume control via dial

Summary

This PR adds a new UserVolume action that allows Stream Deck+ users to adjust individual Discord user volumes directly from a dial while in a voice channel.

This feature is inspired by the Windows Stream Deck Discord plugin and solves a common pain point: Discord doesn't auto-level audio, so users with different microphone settings can have vastly different volume levels. This action allows quick per-user volume adjustments without leaving your current application/game or opening Discord settings.

Features

  • Dial rotation: Adjust the selected user's volume (±5% per tick, range 0-200%)
  • Dial press: Cycle through users in the current voice channel
  • Real-time display: Shows channel name, username/nickname, and current volume percentage
  • Live updates: User list automatically updates when people join/leave the channel
  • Self-filtering: Excludes your own user from the list (can't adjust own volume via Discord RPC)

Usage

  1. Assign the "User Volume" action to a dial on your Stream Deck+
  2. Join a Discord voice channel with other users
  3. The dial display will show:
    • Top: Voice channel name
    • Center: Current user's name
    • Bottom: Volume percentage
  4. Rotate the dial to adjust volume (0-200%)
  5. Press the dial to cycle to the next user

Technical Implementation

New Files:

  • actions/UserVolume.py - Complete action implementation

Modified Files:

  • backend.py - Added voice state subscription methods and user tracking
  • discordrpc/asyncdiscord.py - Added set_user_voice_settings() and get_channel() RPC methods
  • main.py - Registered new action, fixed callback method naming bug

Key Technical Details:

  • Uses Discord RPC SET_USER_VOICE_SETTINGS command for volume control
  • Subscribes to VOICE_STATE_CREATE/DELETE/UPDATE events with channel-specific subscriptions
  • Fetches initial user list via GET_CHANNEL response
  • Dedicated subscribe_voice_states() / unsubscribe_voice_states() backend methods to work around IPC serialization limitations

Bug Fix Included

Also fixes callback cleanup bug where DiscordCore.cleanup_callbacks() called remove_callback() but main.py defined clear_callbacks(). Renamed to remove_callback() for consistency with add_callback().

Testing Status

Completed:

  • ✅ Single user volume adjustment (0-200% range)
  • ✅ User join/leave detection and list updates
  • ✅ Channel switching
  • ✅ Self-user filtering

Pending:

  • ⚠️ Multi-user cycling (3+ users)
  • ⚠️ Concurrent volume adjustments
  • ⚠️ Edge cases with rapid user changes

Full testing requires coordination with multiple users in a voice channel. Basic functionality has been verified, but additional testing with 3+ users is recommended before merge.

Add UserVolume action that allows adjusting individual user volumes
while in a Discord voice channel using a Stream Deck+ dial.

Features:
- Dial rotation adjusts selected user's volume (±5% per tick, 0-200%)
- Dial press cycles through users in the voice channel
- Display shows channel name, username, and volume percentage
- Real-time updates when users join/leave the channel

Also fixes callback cleanup bug in main.py: DiscordCore.cleanup_callbacks()
was calling remove_callback() but main.py defined clear_callbacks().
Renamed to remove_callback() for consistency with add_callback().
@hatzibod hatzibod changed the title feature: add per-user volume control via dial feat: add per-user volume control via dial Dec 31, 2025
@hatzibod hatzibod changed the title feat: add per-user volume control via dial feat(action): add per-user volume control via dial Dec 31, 2025
backend.py Outdated
return self._is_authed

def register_callback(self, key: str, callback: callable):
def register_callback(self, key: str, callback: callable, args: dict = None):
Copy link
Owner

Choose a reason for hiding this comment

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

I don't see anywhere this args argument is passed in, am I missing something?

@ImDevinC
Copy link
Owner

ImDevinC commented Jan 1, 2026

One small note, you'll need to update the manifest.json as well (i need to automate that at some point). Since this is a feat commit, you can just bump from 1.9.x to 1.10.0

…d.py

- Bump version from 1.9.2 to 1.10.0 in manifest.json
- Revert callback rename (remove_callback -> clear_callbacks) to be handled in separate bug-fix PR
@hatzibod
Copy link
Contributor Author

hatzibod commented Jan 1, 2026

I haven't been able to test the functionality with more than 2 users in the voice chat yet. The expected behavior is that you can cycle between users by pressing the dial. I'm keeping this as a draft until I can verify this works, but happy to merge if you're able to test it or if you think it's okay to merge as-is.

@ImDevinC
Copy link
Owner

ImDevinC commented Jan 1, 2026

I don't have an SD+ to test with, but what you've done so far looks great. Feel free to remove draft status (you can ignore the GitHub actions error above)

@hatzibod hatzibod marked this pull request as ready for review January 1, 2026 14:36
@ImDevinC ImDevinC merged commit d4b1aa2 into ImDevinC:main Jan 1, 2026
1 of 2 checks passed
@ImDevinC
Copy link
Owner

ImDevinC commented Jan 1, 2026

Thanks again! This is now published as 1.10.0 on the StreamController store

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants