feat(mic): client microphone passthrough (Steam Streaming Microphone) rebased onto v1.17.0 - #293
feat(mic): client microphone passthrough (Steam Streaming Microphone) rebased onto v1.17.0#293vladbogun1 wants to merge 8 commits into
Conversation
…ring
Port of xenstalker02/Vibepollo Steam Streaming Microphone passthrough onto
newest Nonary/Vibepollo base (v1.17.0). This checkpoint adds the standalone
mic engine files and wires them into the Windows build. Shared-file hunks
(stream.cpp routing, platform/windows/audio.cpp WASAPI) still to port.
New files (copied from xen/master):
- src/platform/windows/mic_write.{cpp,h}
- src/platform/windows/vibepollo_vmic.{cpp,h}
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hand-merged xenstalker02/Vibepollo mic passthrough onto the newest Nonary
base (v1.17.0). Mic-only changes; rebranding/installer/playnite churn dropped.
Resolved via git apply --3way + manual conflict resolution:
- config.{h,cpp}: mic_sink / mic_capture_device / mic_buffer_ms / mic_buffer_packets
(kept install_steam_drivers, unlike xen)
- stream.{h,cpp}: 0x3003 mic packet type, session_t::mic jitter-buffer state,
init_mic_passthrough(), IDX_MIC_AUDIO_DATA handler via server->map(),
get_mic_status(); kept v1.17.0 decode_control_packet/control_packet_view_t model
- platform/common.h: audio_control_t mic interface (switch_capture_to,
snapshot_capture_defaults, restore_capture_from, restore_default_render_if_virtual,
init/release_mic_redirect_device) with safe non-Windows defaults
- platform/windows/audio.cpp: WASAPI capture-switch + render-restore; region-4
reconstructed to keep v1.17.0 jthread pending-restore + add xen render guard,
adapted to 2-arg reset_default_device_impl
- new files: platform/windows/mic_write.{cpp,h} (WASAPI render to Steam Streaming
Microphone) + vibepollo_vmic.{cpp,h}; wired into windows.cmake
- nvhttp/audio.cpp: kept v1.17.0 (HDR + safe parsing)
Web UI mic status card (confighttp) intentionally deferred — not needed for the
audio path. Builds unsigned via mic-ci.yml (workflow_dispatch, Windows only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
stream.cpp teardown used xen-only proc::is_placebo_app() and system_tray::update_tray_idle() (absent in v1.17.0). Revert to v1.17.0's update_tray_pausing/stopped. Non-mic churn pulled in by full-file 3way. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…dio.cpp xen mic code used unqualified to_utf8/from_utf8 (resolved via a using-decl in xen's tree); v1.17.0 needs explicit utf_utils:: qualification. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-Werror=cpp made the 'include winsock2.h before windows.h' warning fatal; mmdeviceapi.h/Audioclient.h pull windows.h. Mirrors v1.17.0 audio.cpp order. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts: # src/stream.cpp
Hey @vladbogun1 — this is awesome, thank you. And thanks for the really generous credit. Seeing the mic feature rebased cleanly onto v1.17, minus all my fork's rebranding/installer noise, is honestly exactly how I'd have wanted it upstreamed. Two things that might save you time on the client side you mentioned — it already exists: xenstalker02/Vibelight, a moonlight-qt fork. It's the reference implementation for the 0x3003 protocol (Opus 48k mono, 20ms frames, 4-byte header, encrypted control session required), so it should map fairly directly onto a newer base. And I'm glad to answer anything about the protocol or the design calls — the wire format and the downmix/jitter-buffer constants are documented in the repos, and I can point you at specific hardening fixes if any are relevant to the port. Really appreciate you carrying this upstream. Glad it's useful to more people. 🙏 |
|
Thanks a lot for commenting, I just did it a day before I went on vacation with my steam deck and did a quick initial merging latest updates on vibepollo because I need a micro during vacation. Now yeah, I also have plans to do same with client, and most needed for me is Android app, so I'll take a look on that first, thanks for useful links. For 2 weeks on my PC this build works pretty stable btw. |
|
As a user, I’d love to see microphone support land here! Currently, mic support is scattered across various forks with fragmented implementations. If this PR provides a clean, generic server protocol (or aligns with existing popular client implementations), I'm sure third-party clients will naturally adapt to it without putting any client-maintenance burden on this repo. Really hope this gets considered! |
|
@vladbogun1 how is compatibility with foundation-sunshine and punktfunk?
being compatible with foundation-sunshine is likely more important since afair punktfunk mic support isn't in the best state rn https://github.com/AlkaidLab/foundation-sunshine |
# Conflicts: # src/platform/common.h # src/platform/windows/audio.cpp
|
Thanks @000MDK, @Akikazehub — fair points, worth being explicit about. First, on what this PR is: it's not a new protocol. It's a faithful rebase of @xenstalker02's existing On compatibility with foundation-sunshine / punktfunk — honestly, no, they're different wire formats. From what I can see, foundation-sunshine + qiin2333's client use a On which protocol should win — I think that's genuinely a call for @Nonary and the project, not something I can settle by merging this. I'm a contributor who needed mic on my Steam Deck and rebased the cleanest existing implementation. A few honest tradeoffs for whoever makes that call:
If the project wants to standardize on foundation-sunshine's protocol for the client breadth, that's a defensible direction — but it's a separate, larger effort (a host-side reimplementation against a less-documented spec), not a change to this PR. The value here is a working, minimal, verifiable mic path today that can be aligned to whatever direction @Nonary settles on. @xenstalker02 — you designed |
|
Happy to weigh in, though my answer argues against my own protocol. Don't standardize on The tradeoff list above has one thing slightly off, and that's my fault for never documenting it clearly: Two more things that are fine for a fork feature and bad for a spec:
Compare what logabell's fork actually exposes: What shouldn't get thrown away is @vladbogun1's PR. The wire format is the cheapest part of it. The host-side receive path is the expensive part, and it's transport-agnostic: the jitter buffer, lazy init on first packet, the WASAPI default-capture switch with its render-restore guard, the self-heal for when Windows has the Steam Streaming Microphone endpoint disabled (that one cost me a day and presented exactly like a protocol bug), and the mono downmix that has to average rather than clamped-sum or it clips. Re-point that at One piece of The wire format and the failure modes are already written down in the Vibelight README and CHANGELOG — the packet layout, the 248-byte cap, the downmix, the endpoint self-heal. Happy to answer questions against those; whichever transport wins, that crash list is worth more than the protocol. |
|
@vladbogun1 @xenstalker02 thanks for the great responses, really appreciate it! Optimum would be compatibility with all of them, detecting what moonlight provides with foundation compatibility being the most important one - from a more practical pov ofc. I bring this up because of the denial of the other mic PR by logabell, reason given was "feature has no clients supporting it". @Nonary: quick question about your actual requirements to accept this PR: anyways, big thanks to everyone involved, keep up the good work! |
|
I am not sure if I will do this yet, but I am considering forking all major moonlight clients. Android/iOS and PC I think it might be time to do so soon because there is very few things I can improve for streaming with only the host being involved. For example I just fixed VRR for streaming on the PC side |
Client microphone passthrough (Steam Streaming Microphone) on current base
This ports the Steam Streaming Microphone client-mic passthrough from
xenstalker02/Vibepollo(which sits ~1.5 minor versions behind)onto the current Vibepollo base (branched from
v1.17.0). All credit for the original mic implementation andprotocol goes to @xenstalker02; this PR is a clean-room rebase of just the mic feature onto the newest code,
dropping the unrelated rebranding/installer/playnite churn.
What it does
Receives an Opus-encoded microphone stream from a compatible Moonlight client over the encrypted control
stream (
0x3003), decodes + jitter-buffers it, and renders it into the Steam Streaming Microphone virtualrender endpoint so host apps (Discord, etc.) can read it as a normal mic input.
Changes (mic-only)
config.{h,cpp}:mic_sink,mic_capture_device,mic_buffer_ms,mic_buffer_packets(with sane defaults — no config edit required).stream.{h,cpp}:0x3003packet type,session_t::micjitter-buffer state,init_mic_passthrough()(lazy init on first packet),IDX_MIC_AUDIO_DATAhandler viaserver->map(),get_mic_status(). Kept the v1.17.0decode_control_packet/control_packet_view_tmodel.platform/common.h:audio_control_tmic interface (switch_capture_to,snapshot_capture_defaults,restore_capture_from,restore_default_render_if_virtual,init/release_mic_redirect_device) with safe non-Windows defaults.platform/windows/audio.cpp: WASAPI default-capture switch + render-restore guard, merged with v1.17.0'sreset_default_device_impl(bool, preferred)and jthread pending-restore.platform/windows/mic_write.{cpp,h}(WASAPI render to the Steam mic endpoint) +vibepollo_vmic.{cpp,h}; wired intowindows.cmake.Testing
Notes for review
platform/windows/PolicyConfig.halready exists in the base..github/workflows/mic-ci.yml(windows-onlyworkflow_dispatch) was added as a dev build helper and can be dropped on merge.moonlight-qtmic fork on the newest base) is being prepared as a separate effort.