Skip to content
Open
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
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Major features:
- [x] Built-in Virtual Display with HDR support that matches the resolution/framerate config of your client automatically
- [x] Permission management for clients
- [x] Clipboard sync
- [x] Windows remote microphone redirection through Steam Streaming Microphone with host-side debug visibility
- [x] Commands for client connection/disconnection (checkout [Auto pause/resume games](https://github.com/ClassicOldSong/Apollo/wiki/Auto-pause-resume-games))
- [x] Input only mode

Expand Down Expand Up @@ -35,6 +36,14 @@ Apollo uses SudoVDA for virtual display. It features auto resolution and framera

The virtual display works just like any physically attached monitors with SudoVDA, there's completely no need for a super complicated solution to "fix" resolution configurations for your devices. Unlike all other solutions that reuses one identity or generate a random one each time for any virtual display sessions, **Apollo assigns a fixed identity for each Artemis/Moonlight client, so your display configuration will be automatically remembered and managed by Windows natively.**

## About Remote Microphone Redirection

This fork adds a working Windows remote microphone path for compatible Moonlight/Artemis clients.

Apollo accepts the client's Opus microphone packets, decodes them on the host, and renders the audio into the Steam playback endpoint `Speakers (Steam Streaming Microphone)`. Host applications should then select `Microphone (Steam Streaming Microphone)` as their microphone source.

Setup and implementation notes are documented in [docs/remote_microphone.md](docs/remote_microphone.md).

## Configuration for dual GPU laptops

Apollo supports dual GPUs seamlessly.
Expand Down
4 changes: 4 additions & 0 deletions cmake/compile_definitions/windows.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ set(PLATFORM_TARGET_FILES
"${CMAKE_SOURCE_DIR}/src/platform/windows/display_ram.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/display_wgc.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/audio.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/apollo_vmic.h"
"${CMAKE_SOURCE_DIR}/src/platform/windows/apollo_vmic.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/mic_write.h"
"${CMAKE_SOURCE_DIR}/src/platform/windows/mic_write.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/virtual_display.h"
"${CMAKE_SOURCE_DIR}/src/platform/windows/virtual_display.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/windows/utils.h"
Expand Down
11 changes: 6 additions & 5 deletions cmake/dependencies/Boost_Sunshine.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
#
include_guard(GLOBAL)

set(BOOST_VERSION "1.89.0")
set(BOOST_MIN_VERSION "1.89.0")
set(BOOST_FETCH_VERSION "1.89.0")
set(BOOST_COMPONENTS
filesystem
locale
Expand All @@ -30,9 +31,9 @@ endif()
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.30")
cmake_policy(SET CMP0167 NEW) # Get BoostConfig.cmake from upstream
endif()
find_package(Boost CONFIG ${BOOST_VERSION} EXACT COMPONENTS ${BOOST_COMPONENTS})
find_package(Boost CONFIG ${BOOST_MIN_VERSION} COMPONENTS ${BOOST_COMPONENTS})
if(NOT Boost_FOUND)
message(STATUS "Boost v${BOOST_VERSION} package not found in the system. Falling back to FetchContent.")
message(STATUS "Boost v${BOOST_MIN_VERSION}+ package not found in the system. Falling back to FetchContent.")
include(FetchContent)

if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
Expand All @@ -54,8 +55,8 @@ if(NOT Boost_FOUND)

# Limit boost to the required libraries only
set(BOOST_INCLUDE_LIBRARIES ${BOOST_COMPONENTS})
set(BOOST_URL "https://github.com/boostorg/boost/releases/download/boost-${BOOST_VERSION}/boost-${BOOST_VERSION}-cmake.tar.xz") # cmake-lint: disable=C0301
set(BOOST_HASH "SHA256=f48b48390380cfb94a629872346e3a81370dc498896f16019ade727ab72eb1ec")
set(BOOST_URL "https://github.com/boostorg/boost/releases/download/boost-${BOOST_FETCH_VERSION}/boost-${BOOST_FETCH_VERSION}-cmake.tar.xz") # cmake-lint: disable=C0301
set(BOOST_HASH "SHA256=67acec02d0d118b5de9eb441f5fb707b3a1cdd884be00ca24b9a73c995511f74")

if(CMAKE_VERSION VERSION_LESS "3.24.0")
FetchContent_Declare(
Expand Down
17 changes: 14 additions & 3 deletions cmake/targets/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,18 @@ else()
endif()

#WebUI build
find_program(NPM npm REQUIRED)
if(WIN32)
unset(NODE CACHE)
unset(NPM_CLI CACHE)
find_program(NODE node.exe REQUIRED)
find_file(NPM_CLI npm-cli.js
PATHS
"${CMAKE_PREFIX_PATH}"
"C:/msys64/ucrt64/lib/node_modules/npm/bin"
REQUIRED)
else()
find_program(NPM npm REQUIRED)
endif()

if (NPM_OFFLINE)
set(NPM_INSTALL_FLAGS "--offline")
Expand All @@ -63,8 +74,8 @@ endif()
add_custom_target(web-ui ALL
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Installing NPM Dependencies and Building the Web UI"
COMMAND "$<$<BOOL:${WIN32}>:cmd;/C>" "${NPM}" install ${NPM_INSTALL_FLAGS}
COMMAND "${CMAKE_COMMAND}" -E env "SUNSHINE_BUILD_HOMEBREW=${NPM_BUILD_HOMEBREW}" "SUNSHINE_SOURCE_ASSETS_DIR=${NPM_SOURCE_ASSETS_DIR}" "SUNSHINE_ASSETS_DIR=${NPM_ASSETS_DIR}" "$<$<BOOL:${WIN32}>:cmd;/C>" "${NPM}" run build # cmake-lint: disable=C0301
COMMAND "$<$<BOOL:${WIN32}>:${NODE};${NPM_CLI}>$<$<NOT:$<BOOL:${WIN32}>>:${NPM}>" install ${NPM_INSTALL_FLAGS}
COMMAND "${CMAKE_COMMAND}" -E env "SUNSHINE_BUILD_HOMEBREW=${NPM_BUILD_HOMEBREW}" "SUNSHINE_SOURCE_ASSETS_DIR=${NPM_SOURCE_ASSETS_DIR}" "SUNSHINE_ASSETS_DIR=${NPM_ASSETS_DIR}" "$<$<BOOL:${WIN32}>:${NODE};${NPM_CLI}>$<$<NOT:$<BOOL:${WIN32}>>:${NPM}>" run build # cmake-lint: disable=C0301
COMMAND_EXPAND_LISTS
VERBATIM)

Expand Down
73 changes: 73 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,56 @@ editing the `conf` file in a text editor. Use the examples as reference.
</tr>
</table>

### mic_backend

<table>
<tr>
<td>Description</td>
<td colspan="2">
Select how Apollo exposes redirected client microphone audio on Windows.
In this fork, Windows microphone redirection is standardized on the `steam_streaming_microphone` backend.
Apollo renders decoded client microphone audio into the Steam playback endpoint, and host applications
should select the paired `Microphone (Steam Streaming Microphone)` recording device.
@note{This option is currently only used on Windows hosts.}
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}
steam_streaming_microphone
@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
mic_backend = steam_streaming_microphone
@endcode</td>
</tr>
</table>

### mic_device

<table>
<tr>
<td>Description</td>
<td colspan="2">
The host-side device used for redirected client microphone audio.
On Windows, Apollo currently auto-detects the Steam Streaming Microphone render endpoint and this value is typically left unset.
On Linux and macOS this should point at the virtual device Apollo writes into.
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">Unset.</td>
</tr>
<tr>
<td>Example (Linux)</td>
<td colspan="2">@code{}
mic_device = sunshine-mic
@endcode</td>
</tr>
</table>

### stream_audio

<table>
Expand All @@ -848,6 +898,29 @@ editing the `conf` file in a text editor. Use the examples as reference.
</tr>
</table>

### stream_mic

<table>
<tr>
<td>Description</td>
<td colspan="2">
Whether Apollo should accept redirected client microphone audio and inject it into a host-side microphone backend.
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}
disabled
@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
stream_mic = enabled
@endcode</td>
</tr>
</table>

### install_steam_audio_drivers

<table>
Expand Down
67 changes: 67 additions & 0 deletions docs/remote_microphone.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Remote Microphone Support

This fork adds a working host-side remote microphone path for Apollo, focused on Windows hosts and Steam Streaming Microphone integration.

## Overview

The microphone path is:

1. A compatible Moonlight/Artemis client captures local microphone audio.
2. The client sends encrypted or unencrypted microphone packets to Apollo on the dedicated microphone stream.
3. Apollo receives the packets, decrypts them when needed, and decodes the Opus frames on the host.
4. Apollo renders the decoded PCM into the Steam playback endpoint `Speakers (Steam Streaming Microphone)`.
5. Host applications consume that audio from the paired capture endpoint `Microphone (Steam Streaming Microphone)`.

This keeps the host-side application flow simple: Apollo writes into Steam Streaming Microphone, and games, chat apps, or capture tools use `Microphone (Steam Streaming Microphone)` as the microphone.

## What Changed

The working implementation in this fork includes:

- Dedicated microphone session handling in the stream path, including packet receive, optional decryption, and per-session lifecycle management.
- Windows microphone backend initialization and teardown that stays alive for the full remote microphone session.
- A Steam-backed Windows microphone path that auto-detects the Steam microphone render/capture pair, normalizes only that pair to `2ch, 32-bit, 48000 Hz` when microphone streaming starts, decodes Opus microphone frames as mono float `48 kHz`, and writes them into the Steam microphone render buffer using a `float32` shared-mode render client.
- Host-side recovery for recoverable WASAPI failures such as device invalidation or audio service restarts.
- A Remote Microphone Debug panel in the web UI that shows packet arrival, decode status, render status, signal detection, counters, and recent mic events.

## Key Files

- `src/stream.cpp`: microphone socket handling, session startup/shutdown, and packet routing.
- `src/audio.cpp`: shared microphone debug state and persistent audio context ownership for the redirect device.
- `src/platform/windows/audio.cpp`: Windows microphone backend selection and redirect device ownership.
- `src/platform/windows/apollo_vmic.cpp`: Steam Streaming Microphone backend wrapper.
- `src/platform/windows/mic_write.cpp`: device discovery, WASAPI initialization, Opus decode, and Steam Streaming Microphone rendering.
- `src_assets/common/assets/web/configs/tabs/AudioVideo.vue`: Remote Microphone Debug UI.

## Windows Requirements

- Install the Steam audio drivers on the host.
- Ensure the playback endpoint `Speakers (Steam Streaming Microphone)` exists and is enabled.
- In host applications, select `Microphone (Steam Streaming Microphone)` as the microphone/recording source.
- Enable `stream_mic` in Apollo.
- Use a client build that supports Apollo microphone redirection.

## Configuration Notes

- `stream_mic` enables the host microphone redirect path.
- `mic_backend` defaults to `steam_streaming_microphone` on Windows in this fork.
- On Windows, Apollo auto-detects the Steam Streaming Microphone pair and normalizes only those microphone endpoints to `2ch, 32-bit, 48000 Hz` automatically instead of requiring a manual device-properties change.
- `mic_device` is mainly relevant on non-Windows platforms. The Windows path currently targets Steam Streaming Microphone automatically.
- Redirected microphone transport is always required to negotiate encrypted microphone packets. If the client falls back to plaintext microphone transport, Apollo disables microphone passthrough for that session instead of accepting unencrypted microphone packets.

## Debugging

The Audio/Video page on Windows exposes a Remote Microphone Debug panel that shows:

- whether the client is sending packets
- whether Apollo is decoding microphone frames
- whether Apollo is rendering into Steam Streaming Microphone
- whether non-silent input is being detected
- which endpoint mix format Apollo discovered
- which render and capture device formats are currently active
- which render format Apollo actually initialized
- whether the recommended Steam microphone format is active or had to be enforced
- how mono input is mapped to the host channels
- the most recent mic errors and recent mic events

This view is intended to quickly separate client capture problems from host decode/render problems.
Loading