Skip to content

Commit 44eaef7

Browse files
jensenpatcodex
andauthored
[windows] Embed DFNR model and set Store identity (#3225)
## Summary This prepares the Windows Store MSIX package for submission while keeping DFNR enabled without shipping the DeepFilterNet model as a `.tar.gz` archive. - Embed the DeepFilterNet model resources at build time so Store packages can load DFNR data directly from Qt resources. - Update the Windows MSIX packaging script and CI workflow to exclude the loose DFNR model payload for Store builds. - Add checked-in Partner Center package identity defaults for the Store submission: - `Identity.Name`: `AetherSDR.AetherSDR` - `Identity.Publisher`: `CN=E03F94A2-AEAB-46D2-8BF1-6419C305CC44` - `PublisherDisplayName`: `AetherSDR` - Document the Store build flow, identity defaults, validation steps, and upload package behavior. ## Implementation Notes The DFNR loader now first tries the existing app-data model location, then falls back to a packaged resource path when the model is embedded. This lets normal development/test builds continue to use external model files while Store builds avoid archive payloads that WACK flags. The workflow sources `packaging/windows/store-identity.ps1` before creating the MSIX. The helper only fills missing environment variables, so CI repository variables or local shell variables can still override the identity when needed. ## Validation Built locally from a fresh Windows Store submission worktree using the MSVC environment and 8 build jobs. - `cmake --build build\msvc-store-submit --target AetherSDR -j 8` - `windeployqt deploy\AetherSDR.exe --release --no-translations --no-system-d3d-compiler` - `packaging\windows\create-msix.ps1 -DeployDir deploy -PackageRoot AetherSDR-store-submission\msix-root -OutputDir AetherSDR-store-submission -CreateUpload -SkipSign -ExcludeDfnrModel` - `packaging\windows\check-dpi-awareness.ps1 -ExePath deploy\AetherSDR.exe` - Windows App Certification Kit on the final MSIX Final local validation results: - Manifest identity verified as `AetherSDR.AetherSDR` / `CN=E03F94A2-AEAB-46D2-8BF1-6419C305CC44` / `AetherSDR`. - Final package contains `deepfilter.dll` and `AetherSDR.exe`, but no `DeepFilterNet`, `.dfmodel`, or `.tar.gz` payload. - WACK overall result: `PASS`. - WACK `Archive files usage`: `PASS`. - WACK `DPIAwarenessValidation`: `PASS`. - WACK optional `Blocked executables`: still reports findings from Qt/OpenGL/process API/static string matches, but overall WACK remains `PASS`. The final local artifact for Partner Center submission is the generated `.msixupload`; it is intentionally unsigned with `-SkipSign` because Microsoft signs Store-distributed packages. --------- Co-authored-by: Codex <noreply@openai.com>
1 parent 9d89873 commit 44eaef7

6 files changed

Lines changed: 232 additions & 58 deletions

File tree

.github/workflows/windows-installer.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,8 @@ jobs:
114114
if (Test-Path "third_party\deepfilter\lib\windows-x86_64\deepfilter.dll") {
115115
copy third_party\deepfilter\lib\windows-x86_64\deepfilter.dll deploy\
116116
}
117-
if (Test-Path "third_party\deepfilter\models\DeepFilterNet3_onnx.tar.gz") {
118-
copy third_party\deepfilter\models\DeepFilterNet3_onnx.tar.gz deploy\
119-
}
117+
# Windows builds embed the DFNR model in AetherSDR.exe resources, so
118+
# no loose DeepFilterNet3_onnx.tar.gz payload is deployed.
120119
# NOTE: MQTT TLS is disabled (-DMQTT_TLS=OFF) so no OpenSSL DLLs
121120
# are needed. This avoids the "libssl-3-x64.dll not found" crash
122121
# on systems without OpenSSL installed (#1341, #1362).
@@ -170,6 +169,7 @@ jobs:
170169
AETHERSDR_MSIX_INSTALLER_ACCENT_COLOR: ${{ vars.AETHERSDR_MSIX_INSTALLER_ACCENT_COLOR }}
171170
AETHERSDR_MSIX_INSTALLER_BACKGROUND_COLOR: ${{ vars.AETHERSDR_MSIX_INSTALLER_BACKGROUND_COLOR }}
172171
run: |
172+
. .\packaging\windows\store-identity.ps1
173173
powershell -NoProfile -ExecutionPolicy Bypass -File packaging\windows\create-msix.ps1 -DeployDir deploy -OutputDir . -CreateUpload -SkipSign -ExcludeDfnrModel
174174
175175
- name: Upload artifacts

CMakeLists.txt

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ option(ENABLE_DFNR "Enable DFNR DeepFilterNet3 noise reduction" ON)
2929
option(ENABLE_MQTT "Enable MQTT client support" ON)
3030
option(MQTT_TLS "Enable MQTT TLS via OpenSSL (disable for AppImage)" ON)
3131
option(LOWER_CASE_BINARY_NAME "Make the output binary lower case. Only affects Linux" OFF)
32+
if(WIN32)
33+
set(AETHER_EMBED_DFNR_MODEL_DEFAULT ON)
34+
else()
35+
set(AETHER_EMBED_DFNR_MODEL_DEFAULT OFF)
36+
endif()
37+
option(AETHER_EMBED_DFNR_MODEL
38+
"Embed the DFNR model payload into application resources instead of deploying a loose archive"
39+
${AETHER_EMBED_DFNR_MODEL_DEFAULT})
3240

3341
# macOS: ensure Homebrew lib/include paths are in the search path
3442
# (universal builds with CMAKE_OSX_ARCHITECTURES may not search /opt/homebrew by default)
@@ -397,9 +405,11 @@ else()
397405
endif()
398406

399407
# DeepFilterNet3 DFNR — bundled neural noise reduction (MIT/Apache-2.0)
400-
# Pre-built static library from Rust crate (libdf), model weights embedded.
408+
# Pre-built libdf library from the Rust crate; model payload is handled below.
401409
if(ENABLE_DFNR)
402410
set(DEEPFILTER_DIR ${CMAKE_SOURCE_DIR}/third_party/deepfilter)
411+
set(DFNR_MODEL "${DEEPFILTER_DIR}/models/DeepFilterNet3_onnx.tar.gz")
412+
set(DFNR_EMBEDDED_MODEL_NAME "DeepFilterNet3_onnx.dfmodel")
403413
if(WIN32)
404414
set(DFNR_LIB_DIR "${DEEPFILTER_DIR}/lib/windows-x86_64")
405415
if(MINGW)
@@ -899,9 +909,28 @@ if(NOT MSVC)
899909
endif()
900910
endif() # NOT MSVC
901911

912+
set(DFNR_RESOURCES "")
913+
if(ENABLE_DFNR AND AETHER_EMBED_DFNR_MODEL)
914+
if(EXISTS "${DFNR_MODEL}")
915+
file(TO_CMAKE_PATH "${DFNR_MODEL}" DFNR_MODEL_QRC_PATH)
916+
set(DFNR_MODEL_QRC "${CMAKE_CURRENT_BINARY_DIR}/dfnr_model.qrc")
917+
file(WRITE "${DFNR_MODEL_QRC}"
918+
"<RCC>
919+
<qresource prefix=\"/models\">
920+
<file alias=\"${DFNR_EMBEDDED_MODEL_NAME}\">${DFNR_MODEL_QRC_PATH}</file>
921+
</qresource>
922+
</RCC>
923+
")
924+
qt_add_resources(DFNR_RESOURCES "${DFNR_MODEL_QRC}")
925+
message(STATUS "DFNR model will be embedded in Qt resources")
926+
else()
927+
message(WARNING "AETHER_EMBED_DFNR_MODEL is ON, but DFNR model not found at ${DFNR_MODEL}")
928+
endif()
929+
endif()
930+
902931
qt_add_resources(RESOURCES resources/resources.qrc)
903932

904-
add_executable(AetherSDR ${ALL_SOURCES} ${RNNOISE_SOURCES} ${GGMORSE_SOURCES} ${RADE_SOURCES} ${BNR_SOURCES} ${SPECBLEACH_SOURCES} ${RESOURCES})
933+
add_executable(AetherSDR ${ALL_SOURCES} ${RNNOISE_SOURCES} ${GGMORSE_SOURCES} ${RADE_SOURCES} ${BNR_SOURCES} ${SPECBLEACH_SOURCES} ${RESOURCES} ${DFNR_RESOURCES})
905934

906935
if (USE_SYSTEM_ZLIB)
907936
target_link_libraries(AetherSDR PRIVATE PkgConfig::zlib)
@@ -1377,8 +1406,7 @@ if(ENABLE_DFNR)
13771406
"${DFNR_DLL}" "$<TARGET_FILE_DIR:AetherSDR>"
13781407
COMMENT "Copying deepfilter.dll to build directory")
13791408
endif()
1380-
set(DFNR_MODEL "${DEEPFILTER_DIR}/models/DeepFilterNet3_onnx.tar.gz")
1381-
if(EXISTS ${DFNR_MODEL})
1409+
if(EXISTS "${DFNR_MODEL}" AND NOT AETHER_EMBED_DFNR_MODEL)
13821410
if(APPLE)
13831411
# macOS: place in Resources/ so notarization doesn't reject a
13841412
# non-Mach-O file inside Contents/MacOS/
@@ -1396,7 +1424,7 @@ if(ENABLE_DFNR)
13961424
endif()
13971425
endif()
13981426
# Install the model alongside the binary for cmake --install
1399-
if(NOT APPLE)
1427+
if(NOT APPLE AND NOT AETHER_EMBED_DFNR_MODEL)
14001428
install(FILES "${DFNR_MODEL}"
14011429
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/AetherSDR"
14021430
OPTIONAL)

docs/WINDOWS-STORE-MSIX.md

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,16 @@ manifest, visual assets, signing, and Store upload wrapper around it.
1313

1414
The script creates `msix-root/`, writes `AppxManifest.xml`, generates package
1515
icons from `docs/assets/logo-circle.png`, adds App Installer UX metadata, runs
16-
`makeappx.exe`, optionally omits the DFNR model archive for Store readiness,
16+
`makeappx.exe`, optionally omits loose DFNR model payloads for Store readiness,
1717
optionally signs the MSIX with `signtool.exe`, and optionally creates a
1818
`.msixupload` archive for Partner Center.
1919

20+
Windows DFNR builds embed the DeepFilterNet model payload in Qt resources by
21+
default (`AETHER_EMBED_DFNR_MODEL=ON`). At runtime, AetherSDR extracts that
22+
payload to writable app-local data as `DeepFilterNet3_onnx.dfmodel` because the
23+
DeepFilter C API requires a filesystem path. This keeps DFNR available in Store
24+
MSIX builds without packaging a loose `DeepFilterNet3_onnx.tar.gz` file.
25+
2026
Development package:
2127

2228
```powershell
@@ -26,10 +32,7 @@ powershell -NoProfile -ExecutionPolicy Bypass -Command ". 'C:\Users\patj\Documen
2632
Store identity package:
2733

2834
```powershell
29-
$env:AETHERSDR_MSIX_IDENTITY_NAME = "PartnerCenter.Assigned.Name"
30-
$env:AETHERSDR_MSIX_PUBLISHER = "CN=Partner-Center-Assigned-Publisher"
31-
$env:AETHERSDR_MSIX_PUBLISHER_DISPLAY_NAME = "AetherSDR"
32-
powershell -NoProfile -ExecutionPolicy Bypass -Command ". 'C:\Users\patj\Documents\AetherSDR\scripts\enter-msvc.ps1' -Arch x64; & '.\packaging\windows\create-msix.ps1' -DeployDir deploy -OutputDir . -CreateUpload -ExcludeDfnrModel"
35+
powershell -NoProfile -ExecutionPolicy Bypass -Command ". 'C:\Users\patj\Documents\AetherSDR\scripts\enter-msvc.ps1' -Arch x64; . '.\packaging\windows\store-identity.ps1'; & '.\packaging\windows\create-msix.ps1' -DeployDir deploy -OutputDir . -CreateUpload -SkipSign -ExcludeDfnrModel"
3336
```
3437

3538
## Manifest Values
@@ -44,16 +47,18 @@ Values we can automate:
4447
because the manifest uses `uap10:RuntimeBehavior`.
4548
- Visual assets: generated from the existing AetherSDR logo.
4649

47-
Values that must come from Partner Center for the Store package:
50+
Partner Center Store package values:
51+
52+
- `Identity.Name`: `AetherSDR.AetherSDR`
53+
- `Identity.Publisher`: `CN=E03F94A2-AEAB-46D2-8BF1-6419C305CC44`
54+
- `PublisherDisplayName`: `AetherSDR`
4855

49-
- `Identity.Name`: assigned after reserving the product name.
50-
- `Identity.Publisher`: assigned by Partner Center and tied to the developer
51-
account/certificate identity.
56+
These values are in `packaging/windows/store-identity.ps1`. The file only sets
57+
variables that are currently unset, so CI repository variables or local shell
58+
environment variables can still override them for testing.
5259

5360
Values that need maintainer choice:
5461

55-
- Public Store/product name: likely `AetherSDR`, but confirm before reserving.
56-
- `PublisherDisplayName`: `AetherSDR`.
5762
- Short manifest description:
5863
`Multi-platform SDR client for FlexRadio transceivers (6000/8600/Aurora).`
5964
- Capability disclosure comfort:
@@ -69,14 +74,13 @@ Already automatable:
6974

7075
- Local MSIX creation from the existing Windows deploy folder.
7176
- CI artifact creation after the current `windeployqt` deployment step.
72-
- Store identity injection from GitHub repository variables.
77+
- Store identity injection from `packaging/windows/store-identity.ps1`, with
78+
optional GitHub repository variable overrides.
7379
- Signing from GitHub secrets when a development/test PFX exists.
7480
- `.msixupload` creation for Partner Center.
7581

7682
Not fully automatable until account setup:
7783

78-
- Reserving the Store product name.
79-
- Copying the Partner Center package identity values into GitHub variables.
8084
- Final Store submission unless Partner Center API credentials are created and
8185
stored as secrets.
8286

@@ -87,12 +91,10 @@ signals, but some findings need follow-up before final submission:
8791

8892
- `Blocked executables`: AetherSDR shells out to PowerShell for Windows support
8993
bundle ZIP creation. Replace that path with in-process ZIP creation.
90-
- `Archive files usage`: Store MSIX builds pass `-ExcludeDfnrModel` so the
91-
package does not include `DeepFilterNet3_onnx.tar.gz`. The archive contains
92-
`enc.onnx`, `erb_dec.onnx`, `df_dec.onnx`, and `config.ini`, but the current
93-
DeepFilter C API expects the tar.gz path. Check whether `df_create(nullptr,
94-
...)` can use the embedded default model before restoring DFNR in Store
95-
packages.
94+
- `Archive files usage`: Windows DFNR builds embed the model payload in Qt
95+
resources and do not deploy a loose `DeepFilterNet3_onnx.tar.gz` file. The
96+
`-ExcludeDfnrModel` switch remains as a packaging safety net for older deploy
97+
directories or custom builds with loose DFNR payloads.
9698
- `DPIAwarenessValidation`: AetherSDR.exe now embeds a PerMonitorV2 desktop
9799
app manifest, and the Windows installer workflow verifies the deployed
98100
executable before MSIX packaging. WACK 10.0.26100.7705 reports
@@ -102,8 +104,9 @@ signals, but some findings need follow-up before final submission:
102104

103105
## GitHub Variables
104106

105-
The Windows installer workflow reads these optional repository variables before
106-
building the MSIX artifact:
107+
The Windows installer workflow sources `packaging/windows/store-identity.ps1`
108+
before building the MSIX artifact. These optional repository variables override
109+
the checked-in defaults when set:
107110

108111
- `AETHERSDR_MSIX_IDENTITY_NAME`
109112
- `AETHERSDR_MSIX_PUBLISHER`
@@ -114,9 +117,8 @@ building the MSIX artifact:
114117
- `AETHERSDR_MSIX_INSTALLER_ACCENT_COLOR`
115118
- `AETHERSDR_MSIX_INSTALLER_BACKGROUND_COLOR`
116119

117-
If the identity variables are unset, CI still builds a development package using
118-
`AetherSDR.AetherSDR` and `CN=AetherSDR Development`. That package is useful for
119-
manifest/package validation, but it is not the final Store identity.
120+
If the identity variables are unset, CI now uses the checked-in Partner Center
121+
identity defaults from `store-identity.ps1`.
120122

121123
## Local Sideload Signing
122124

packaging/windows/create-msix.ps1

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,12 @@ Get-ChildItem -LiteralPath $resolvedPackageRoot -File -Filter "vc_redist*.exe" -
327327
Remove-Item -Force
328328

329329
if ($ExcludeDfnrModel) {
330-
$dfnrModels = Get-ChildItem -LiteralPath $resolvedPackageRoot -Recurse -File -Filter "DeepFilterNet3_onnx.tar.gz" -ErrorAction SilentlyContinue
331-
foreach ($model in $dfnrModels) {
332-
Write-Host "Excluding DFNR model from MSIX package: $($model.FullName)"
333-
Remove-Item -LiteralPath $model.FullName -Force
330+
foreach ($filter in @("DeepFilterNet3_onnx.tar.gz", "DeepFilterNet3_onnx.dfmodel")) {
331+
$dfnrModels = Get-ChildItem -LiteralPath $resolvedPackageRoot -Recurse -File -Filter $filter -ErrorAction SilentlyContinue
332+
foreach ($model in $dfnrModels) {
333+
Write-Host "Excluding loose DFNR model payload from MSIX package: $($model.FullName)"
334+
Remove-Item -LiteralPath $model.FullName -Force
335+
}
334336
}
335337
}
336338

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<#
2+
.SYNOPSIS
3+
Set the Partner Center package identity for AetherSDR Store MSIX builds.
4+
5+
.DESCRIPTION
6+
These values are assigned on the Partner Center Product identity page after
7+
reserving the Microsoft Store product name. They are public package
8+
identity values, not signing secrets. Existing environment variables win so
9+
CI/release callers can still override them without editing this file.
10+
#>
11+
12+
if ([string]::IsNullOrWhiteSpace($env:AETHERSDR_MSIX_IDENTITY_NAME)) {
13+
$env:AETHERSDR_MSIX_IDENTITY_NAME = "AetherSDR.AetherSDR"
14+
}
15+
16+
if ([string]::IsNullOrWhiteSpace($env:AETHERSDR_MSIX_PUBLISHER)) {
17+
$env:AETHERSDR_MSIX_PUBLISHER = "CN=E03F94A2-AEAB-46D2-8BF1-6419C305CC44"
18+
}
19+
20+
if ([string]::IsNullOrWhiteSpace($env:AETHERSDR_MSIX_DISPLAY_NAME)) {
21+
$env:AETHERSDR_MSIX_DISPLAY_NAME = "AetherSDR"
22+
}
23+
24+
if ([string]::IsNullOrWhiteSpace($env:AETHERSDR_MSIX_PUBLISHER_DISPLAY_NAME)) {
25+
$env:AETHERSDR_MSIX_PUBLISHER_DISPLAY_NAME = "AetherSDR"
26+
}

0 commit comments

Comments
 (0)