Compare commits

..

104 Commits

Author SHA1 Message Date
Michael Jolley
1d11b732b7 [CmdPal] Fix memory leak in PerformanceWidgetsPage network band items (#48880)
## Summary

Fixes a memory leak in the Performance Monitor dock extension where
`GetItems()` created **new** `ListItem` instances for `_networkUpItem`
and `_networkDownItem` on every call.

## Problem

When the dock subscribes to `ItemsChanged` and calls `GetItems()` to
refresh, the band page path allocates 2 new `ListItem` objects each time
— the old ones are replaced in the fields but never collected (they
remain referenced by the `DockItemViewModel` wrappers until the next
refresh cycle). Under normal operation this leaks ~2 objects/second
indefinitely.

## Fix

Move `_networkUpItem`/`_networkDownItem` creation into the constructor
(matching the pattern used by CPU, Memory, GPU, and Battery items).
`GetItems()` now returns stable references. The `Updated` event handler
already updates their `.Title` properties, which propagates to the UI
via `PropChanged` → `CommandItemViewModel.Model_PropChanged`.

## Validation

- Build succeeds (`Microsoft.CmdPal.Ext.PerformanceMonitor.csproj`)
- Network up/down band items still receive title updates via the
existing `Updated` handler
- No `RaiseItemsChanged()` needed — `ListItem.Title` setter fires
`PropChanged`, which `DockItemViewModel` already observes

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 28e078897a)
2026-06-26 13:35:45 +08:00
Mike Griese
ab4792a4b8 Revert "[CmdPal][Dock] Fix performance meter showing '???' after restart" (#48835)
Reverts microsoft/PowerToys#48682

I'm quite sure that OP did not build or test these changes, and they
should not have merged.

(cherry picked from commit 386a16ff94)
2026-06-26 13:35:45 +08:00
Niels Laute
dc3a9da968 [Shortcut Guide] Prevent overlay crash on section navigation (#48448) (#48481)
## Summary of the Pull Request

Shortcut Guide overlay crashes and closes when navigating between
sidebar sections (e.g. clicking PowerToys, then clicking the Windows
icon to come back). Repro and crash logs in #48448. Crash logs in #48441
show the same propagation path plus a follow-up access violation in
coreclr, indicating exceptions that escape local catches.

This PR fixes the immediate navigation race and adds broader crash
hardening so future exceptions on the UI/background threads are logged
instead of tearing down the overlay.

### Navigation-race fix (commit 1)

Root cause: `WindowSelector_SelectionChanged` calls
`App.TaskBarWindow.Activate()` and then `SetWindowPosition()`
synchronously. `Activate()` runs a reentrant `Window_Activated` →
`BringToFront` → `TaskbarWindow.Activated` chain that can leave
`App.TaskBarWindow.AppWindow` momentarily null, so `SetWindowPosition`
throws `NullReferenceException`.

Because the initial `SelectedItem = MenuItems[0]` is set from
`SetNavItems`, the NRE bubbles up into `InitializeNavItemsAsync`'s catch
block — which sets `_closeType = "InitializationFailed"` and closes the
window. That matches both user-visible symptoms: the overlay "flashes
and disappears" (#48441) on open and "closes when clicking the Windows
icon" (#48448).

Edits in
`src/modules/ShortcutGuide/ShortcutGuide.Ui/ShortcutGuideXAML/MainWindow.xaml.cs`:

- **`SetWindowPosition`**: null-guard `App.TaskBarWindow?.AppWindow` and
skip the taskbar-overlap height adjustment when it is not currently
observable. Wrap the body in `try`/`catch` with `Logger.LogError` so any
future positioning hiccup keeps the previous layout instead of taking
down the overlay.
- **`WindowSelector_SelectionChanged`**: null-guard
`App.TaskBarWindow?.Hide()` / `Activate()` and wrap the body in
`try`/`catch`. This breaks the propagation path that lets a navigation
exception reach `InitializeNavItemsAsync`'s "fatal init failure" branch.

### Crash hardening (commit 2)

Additional defensive changes to cover other unguarded paths surfaced
while reviewing #48441:

- **`App.xaml.cs`**: register `App.UnhandledException`,
`AppDomain.CurrentDomain.UnhandledException`, and
`TaskScheduler.UnobservedTaskException` so a stray exception (e.g. an IO
failure during a fire-and-forget UI handler, or a background `Task`
fault) gets logged instead of tearing the process down with an access
violation in coreclr. Mirrors what Peek, AdvancedPaste, and CmdPal
already do.
- **`App.OnLaunched`**: wrap the launch sequence in `try`/`catch` and
exit cleanly with an error log on failure (`LoadData` / `MainWindow` /
`TaskbarWindow` ctors and `Activate` are all reachable failure points).
- **`App.LoadData`**: broaden the `Pinned.json` deserialize catch to
also handle `IOException` / `UnauthorizedAccessException`, and guard the
round-trip `SaveSettings` call as best-effort with a warning log.
- **`PinnedShortcutsHelper.Save`**: catch `IOException` /
`UnauthorizedAccessException` / `JsonException` and log; `Pin` / `Unpin`
runs from a synchronous UI handler so an unguarded `File.WriteAllText`
would tear down the overlay on any disk hiccup.
- **`TaskbarWindow.UpdateTasklistButtons`**: move the `AppWindow.Move`
calls inside the existing `try` block, null-guard
`App.MainWindow?.AppWindow` up front, and wrap the whole body so the
method (which runs from the ctor and from `Activated`) cannot tear the
overlay down when `MainWindow` is in a transient state.

## PR Checklist

- [x] Closes: #48448
- [x] Likely also fixes: #48441
- [x] **Communication:** Defensive fix to known crash paths; no API
change.
- [ ] **Tests:** No automated tests added — the failures are reentrancy
/ timing races that are hard to deterministically trigger in CI.
Validated with a synthetic repro (see below).
- [x] **Localization:** N/A — only logger strings.
- [x] **Dev docs:** N/A
- [x] **New binaries:** N/A

## Detailed Description of the Pull Request / Additional comments

The fix is intentionally defensive (rather than restructuring the
reentrant activation flow) because the legacy taskbar UIA enumeration
(`TasklistPositions.GetButtons`) on Windows 10 is what most reliably
widens the race window and is out of scope to redesign for a hotfix.

## Validation Steps Performed

- Build: `tools\build\build.cmd` from
`src\modules\ShortcutGuide\ShortcutGuide.Ui` — exit 0, errors log empty
for both commits.
- Synthetic repro: temporarily injected `throw new
NullReferenceException()` at the top of `SetWindowPosition` to exercise
the exact propagation path the reporter's log shows (`SetWindowPosition`
→ `SelectionChanged` → `set_SelectedItem` →
`InitializeNavItemsAsync.catch` → `Close("InitializationFailed")`).
- Before fix: overlay flashes and closes, log shows `Failed to
initialize navigation items.` with the NRE.
- After fix: overlay stays open, page navigates, log shows `Failed to
set Shortcut Guide window position; keeping previous layout.` from the
new `catch`.
- Did not reproduce the live race on a Win11 dev box; the reporter's
repro is Windows 10 19045 / Microsoft Store install where the legacy
taskbar UIA timing makes the reentrant chain more likely to expose the
null.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 502dc40aa4)
2026-06-22 17:15:23 +08:00
moooyo
baee472288 [PowerDisplay] Detect built-in panel when driven by the discrete GPU (#48637)
On dual-GPU laptops, Power Display stopped detecting the built-in panel
(and adjusting its brightness) when the **discrete GPU** drives the
display — it showed "can't detect the display". This fixes that by
classifying displays by **capability** (does WMI brightness work on it?)
instead of by the nominal `OutputTechnology` value, which the discrete
GPU misreports for the internal panel.

- [x] Closes: #48587
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized (no
new user-facing strings added)
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places

On a hybrid / MUX laptop, when the **discrete GPU** drives the built-in
eDP panel, `QueryDisplayConfig` reports the panel's
`DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY` as `DISPLAYPORT_EXTERNAL` (`10`)
instead of the `INTERNAL` flag (`0x80000000`) it reports under the
integrated GPU. It is the *same physical panel* (same EDID) — only the
reported connector type changes with the active GPU.

PR #47740 introduced a strict classifier: `OutputTechnology` →
internal/external, then **internal → WMI-only, external → DDC/CI-only,
with no fallback**. So under the discrete GPU the built-in panel was
classified *external* and sent to DDC/CI only — but a laptop eDP panel
does not speak DDC/CI, so it was dropped and Power Display reported it
couldn't detect any monitor. (`WmiMonitorBrightness` still exposes that
panel regardless of which GPU drives it, so the panel was actually
controllable — it just never got routed to WMI.)

- **`MonitorManager`** now runs **WMI discovery first** over the full
`QueryDisplayConfig` inventory. Every display `WmiMonitorBrightness`
exposes is treated as internal (WMI-controlled); whatever WMI does
**not** claim is routed to DDC/CI. The `OutputTechnology`-based
classifier is gone.
- **`WmiController`** matches the system-wide `WmiMonitorBrightness`
results against the full inventory by `Monitor.Id`. The persisted
`Monitor.Id` is still taken from the matched `DevicePath`
(byte-identical to the DDC route and to prior releases), so saved
brightness/per-monitor settings survive upgrades.
- New **`MonitorIdentity.FromInstanceName`** reduces a WMI
`InstanceName` to the same canonical `Monitor.Id` as `FromDevicePath`;
the separate `PnpHardwareKey` helper is removed.
- **Deleted** `DisplayClassifier` and `MonitorDisplayInfo.IsInternal`
(net ~150 fewer lines).

A monitor that exposes **both** `WmiMonitorBrightness` **and** DDC/CI is
now controlled via WMI only and won't get DDC-only features (contrast /
volume / input source / color temperature / power). This is uncommon
(typical laptop panels are WMI-only; typical external monitors are
DDC-only) and is a deliberate decision: it removes the entire class of
`OutputTechnology` misclassification bugs while keeping the performance
win of not DDC-probing internal panels.

- Built the Power Display app (`PowerDisplay.csproj`) and
`PowerDisplay.Lib.UnitTests` (x64 / Debug) with MSBuild — both succeed,
including after merging latest `main` (Windows App SDK 2.2.0).
- Ran the unit test suite: **128/128 pass**, including new
`FromInstanceName` tests — the `FromInstanceName == FromDevicePath`
equivalence invariant and a concrete #48587 regression case (the BOE
panel reported as `OutputTechnology=10`).
- Traced the fix against the reporter's diagnostic logs: the panel that
previously went `OutputTechnology=10 → External → DDC → dropped` is now
claimed by WMI and controllable.
- Reviewed the diff for regressions (Monitor.Id persistence, monitor
blacklist, mirror mode, dual-internal-panel devices, external-only
desktops).

---------

Co-authored-by: Yu Leng <yuleng@microsoft.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(cherry picked from commit 32ad98a0dd)
2026-06-22 16:30:19 +08:00
Bryce Cindrich
142833d49a fix(shortcut-guide): use <N> token for literal digit keys in manifests (#48757)
## Summary of the Pull Request

Converts the literal-digit shortcut keys in the bundled Shortcut Guide
manifests to the `<N>` special-key convention, so they render as the
correct number.

Per the manifest spec, a bare number in `Keys:` is a virtual-key code. A
literal digit key authored as a bare number is therefore misread (VK `9`
is Tab, VK `1` is the left mouse button, VK `0` is undefined) and
renders incorrectly. The fix authors these as the `<N>` token (for
example `"<9>"`), which `KeyVisual` strips to display the digit.

This is a data-only change: **91 literal-digit keys across 14
manifests** become `<N>` tokens. No code or doc changes; the renderer
and converter already support `<N>`, and the convention is documented in
the spec.

Follow-up to #48461, which introduced and documented the `<N>`
convention (per @noraa-junker's request for a separate PR to fix the
remaining manifests). Together with #48461 this resolves the rendering
reported in #48460.

Files touched (all under
`src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/Manifests/`):
Adobe.Illustrator (18), Adobe.Photoshop (17), SlackTechnologies.Slack
(13), Adobe.InDesign (11), JetBrains.IntelliJIDEA.Community (11),
BlenderFoundation.Blender (3), Figma.Figma (3), Google.Chrome (3),
Microsoft.Edge (3), Microsoft.VisualStudioCode (3), Mozilla.Firefox (3),
+WindowsNT.Notepad (1), Adobe.AfterEffects (1), GIMP.GIMP (1).

## PR Checklist

- [ ] **Closes:** N/A (follow-up to #48461; contributes to #48460)
- [ ] **Communication:** discussed in #48461; @noraa-junker requested
this separate PR.
- [ ] **Tests:** N/A for data; the converter and `<N>` convention are
unit-tested in #48461. Validated here by deserializing every manifest
with YamlDotNet (see below).
- [x] **Localization:** unchanged; these are per-language manifest
files.
- [ ] **Dev docs:** N/A (the `<N>` convention is documented in the spec
via #48461).
- [ ] **New binaries:** N/A.
- [ ] **Documentation updated:** N/A.

## Detailed Description of the Pull Request / Additional comments

Each change wraps a single bare digit in angle brackets, for example:

```yaml
          Keys:
-            - 9
+            - "<9>"
```

Quoted tokens (`"<9>"`) are used to match the dominant special-token
style already in the manifests (`"<Enter>"`, `"<Down>"`, etc.).

Note (out of scope): `Adobe.Photoshop.en-US.yml` has a shortcut with an
empty `Name: ""` (around line 799). That is a pre-existing data issue
unrelated to digit rendering; the digit is still converted, and the
empty name is left as-is.

## Validation Steps Performed

- Confirmed the diff touches only the 14 manifests, 91 insertions and 91
deletions, with each changed line being a digit wrapped as `"<N>"` (no
whitespace, indentation, or encoding churn).
- Confirmed zero bare-digit `Keys` entries remain and exactly 91 new
`"<N>"` tokens exist.
- Deserialized all 32 manifests with YamlDotNet (the same library the
app uses at runtime): 0 parse errors.
- Rendering behavior for `<N>` is already verified in #48461 (the
renderer strips the brackets to show the digit).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit cabb71108a)
2026-06-22 16:29:53 +08:00
Bryce Cindrich
46d594a7f5 feat(shortcut-guide): add Postman manifest and fix numbered-key display (#48461)
## Summary of the Pull Request

Adds a Shortcut Guide manifest for **Postman** and fixes a rendering bug
where single-digit keys in manifests displayed incorrectly.

- **Fix numbered-key rendering** —
`src/modules/ShortcutGuide/ShortcutGuide.Ui/Converters/ShortcutDescriptionToKeysConverter.cs`:
a single digit (`0`–`9`) in a manifest's `Keys` was treated as a Windows
virtual-key code instead of the literal digit. Since VK `1` is the left
mouse button, VK `9` is Tab, and VK `0` is undefined, shortcuts such as
`Ctrl+0` (reset zoom) and `Ctrl+9` (last tab) rendered as
blank/incorrect glyphs. Single digits are now rendered as the literal
character.
- **Add Postman shortcuts** —
`src/modules/ShortcutGuide/ShortcutGuide.Ui/Assets/ShortcutGuide/Manifests/Postman.Postman.en-US.yml`:
new manifest for `Postman.exe` covering Tabs, Sidebar, Request,
Interface, Window and modals, and Console. Auto-included via the
existing `Manifests/*.yml` glob in `ShortcutGuide.Ui.csproj`.
- **Show tab-number ranges** — Edge, Chrome, Firefox, and Postman
manifests: the "switch to a specific tab" entry used the literal key
`1`, which (after the fix above) read as `Ctrl + 1`. It now uses a `1 -
8` range so the keycap conveys "any tab number 1 through 8". The
separate "last tab" (`9`) and "reset zoom" (`0`) entries remain literal
single keys.
- **Add unit tests** — new `ShortcutGuide.UnitTests` (MSTest) project
covering `ShortcutDescriptionToKeysConverter.GetKeysList`, including the
single-digit regression.

## PR Checklist

- [x] Closes: #48460
- [ ] **Communication:** I've discussed this with core contributors
already. <!-- Filed #48460; the v0.100 announcement invites app-shortcut
contributions via PR. -->
- [x] **Tests:** Added/updated and all pass <!-- New
ShortcutGuide.UnitTests (MSTest); 8 tests pass locally via
vstest.console. -->
- [x] **Localization:** All end-user-facing strings can be localized
<!-- Shortcut names live in per-language manifest files (`*.en-US.yml`);
other locales fall back to en-US, consistent with existing manifests.
-->
- [ ] **Dev docs:** Added/updated <!-- N/A: no behavior requiring
dev-doc changes. -->
- [ ] **New binaries:** Added on the required places <!-- N/A: the new
manifest is a data asset under an already-shipped, globbed folder. The
new test project is auto-discovered by the existing `**\*UnitTest*.dll`
VSTest glob, so no CI pipeline change is required. -->
- [ ] **Documentation updated:** <!-- N/A -->

## Detailed Description of the Pull Request / Additional comments

The Shortcut Guide displays per-app shortcuts from YAML manifests,
matched to the foreground window via `WindowFilter`. Keys are converted
to keycaps by `ShortcutDescriptionToKeysConverter`. Numeric key strings
were unconditionally parsed as virtual-key codes, so literal-digit
shortcuts rendered wrong. The fix adds a `>= 0 and <= 9` case that emits
the digit character as-is; non-digit numeric codes (arrows, etc.) are
unchanged.

The new Postman manifest exercises this with `Ctrl+0` / `Ctrl+9`. The
browser/Postman "specific tab" entries were updated from the literal `1`
to the `1 - 8` range string, rendered verbatim by `KeyVisual` (the same
path used by the existing `Number (1-9)` key in the Windows Explorer
manifest).

A new `ShortcutGuide.UnitTests` (MSTest) project covers the converter:
single digits render literally (regression test), modifier ordering,
non-numeric passthrough (e.g. `1 - 8`), and arrow-key VK mapping.

## Validation Steps Performed

Built and ran locally (x64 Debug):

- Built `ShortcutGuideModuleInterface`, `ShortcutGuide.Ui`, and
`ShortcutGuide.IndexYmlGenerator`; launched the Debug `PowerToys.exe`.
- Triggered Shortcut Guide (`Win+Shift+/`) with **Postman** focused: the
Postman section renders with all categories, and `Ctrl+1` / `Ctrl+9` /
`Ctrl+0` display correctly (previously blank/incorrect).
- Verified the "specific tab" entry renders as `Ctrl + 1 - 8` in
**Edge**, **Chrome**, **Firefox**, and **Postman**.
- Built `ShortcutGuide.UnitTests` and ran via `vstest.console.exe`:
**8/8 tests pass**.

<img width="845" height="1432" alt="PowerToys Shortcut Guide Running
Postman"
src="https://github.com/user-attachments/assets/6359617e-3e2c-48b0-8005-b3684594ec94"
/>

Co-Authored-By: Claude Opus 4.8

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit a0e53de825)
2026-06-22 16:29:52 +08:00
Dave Rayment
673a41512c [ColorPicker] Fix the main window UI appearing in the zoomed-in view (#48762)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #37801
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
This uses `SetWindowAffinity()` with the `WDA_EXCLUDEFROMCAPTURE`
constant on the main Color Picker window to ensure the ZoomWindow bitmap
creation excludes the corner of the picker UI. The ability to capture
the picker window is restored immediately after, so it's still visible
in Snipping Tool, Remote Desktop and other tools.

The fix uses a new helper with simple `Include()` / `Exclude()` methods
as an adapter to the native code. This may potentially be included in
Common if other utilities needed it.

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
1. Repro'd the original issue on the current Color Picker release:
<img width="262" height="263" alt="image"
src="https://github.com/user-attachments/assets/bc12b2f8-b67f-4b56-a803-57ab2fe0fa17"
/>

2. Confirmed that the fix worked and normal window affinity was restored
(otherwise I would not have been able to snip this):
<img width="262" height="262" alt="image"
src="https://github.com/user-attachments/assets/bc336ad0-4114-49fa-8440-b78466de363f"
/>

3. Repeated zooming in and out over a normal session.

(cherry picked from commit eab305334b)
2026-06-22 16:29:52 +08:00
Eymard Silva
f57062c206 Fix VS Code Workspaces shared storage lookup (#47505)
## Summary

Fixes VS Code Workspaces recent entries discovery after VS Code 1.118
moved `history.recentlyOpenedPathsList` to the shared application
storage database.

The plugin now probes both the legacy `User/globalStorage/state.vscdb`
database and the new shared storage database for VS Code Stable,
Insiders, Exploration, VSCodium, VSCodium Insiders, and portable
installs.

It also deduplicates results that may appear in both locations.

Fixes #47445

## Validation

- Reviewed the change against the issue's documented VS Code 1.118
storage paths.
- Attempted local focused build from
`src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces`
with `tools/build/build.cmd -Platform x64 -Configuration Debug`.
- Build was blocked by local environment/tooling issues unrelated to
this C# change: missing/invalid VC tooling/Windows SDK.

(cherry picked from commit e1b1a8d7ed)
2026-06-22 16:29:52 +08:00
Mike Griese
f339b48324 CmdPal: Only list available docks when pinning (#48723)
This is a totally minor nitpick.

I don't have the dock enabled on all my displays. But the current "pin
to dock" dialog lets me pin it to a display I don't have the dock on.
When that happens, it effectively results in _nothing_ happening. Not
great.

This PR mitigates that situation, but only listing the enabled docks
when pinning.

(cherry picked from commit 6dcda8a6aa)
2026-06-22 16:29:52 +08:00
ABHIJEET KALE
f590871d6d [CmdPal][Dock] Fix performance meter showing '???' after restart (#48682)
This PR fixes Issue #48680 where the CmdPal dock performance meter shows
'???' after restart.

**Root cause**: The PerformanceWidgetsPage initially returns items with
a placeholder title ('???') because ContentData hasn't loaded yet. The
DataManager timer starts when the dock subscribes to ItemsChanged, and
the Updated event fires 1 second later with real data. However, the
Updated event handlers were updating ListItem.Title directly without
calling RaiseItemsChanged() on the page, so the dock was never notified
that the items had changed and continued to display the stale '???'
title.

**Fix**: Added a RaiseItemsChanged() call to each of the 5 Updated event
handlers (CPU, Memory, Network, GPU, Battery) after the item titles are
updated. This causes the dock to re-call GetItems() and refresh the
displayed titles.

Fixes #48680

(cherry picked from commit 0d12a62abb)
2026-06-22 16:29:52 +08:00
William
bdcedb142e Changed sleep icon to hibernation icon in Command Palette (#48689)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Changed the icon from the sleep icon to the hibernate icon, which fixes
the issue in #48535

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #48535
## Detailed Description of the Pull Request / Additional comments
The icon was previously the sleep icon, and I have changed it to the
hibernate icon.

## Validation Steps Performed
I have visually checked that the icon is now updated to the correct
icon.

(cherry picked from commit 2125d739a3)
2026-06-22 16:29:52 +08:00
Mike Griese
7da85cac40 CmdPal: fix initializing the Run history in AOT builds (#48463)
Regressed in one of the last two releases.

The problem was that `history.ToImmutableList()` ran on the projected
`IVector<string>`. `ImmutableList.CreateRange` checks for
`IReadOnlyCollection<string>`, and resolving that interface on a WinRT
object requires a helper type that AOT can't generate.

So we have to just _not do that_.

Closes #48445

(cherry picked from commit fb0f4292eb)
2026-06-22 16:29:52 +08:00
Clint Rutkas
750ef385b8 [QuickAccess] Suppress unhandled XAML exceptions in flyout host (#48457)
## Summary

Adds the two missing top-level exception handlers in the QuickAccess
(Preview) flyout host so that an unhandled XAML exception during launch
or page navigation no longer FailFasts `PowerToys.QuickAccess.exe`.

Spotted while reading through `App.OnLaunched` and `ShellPage` for an
unrelated review of the flyout startup path — none of the existing
handlers exist yet, so any throw during `MainWindow` construction,
`ShellHost.Initialize`, or `ContentFrame.Navigate(typeof(LaunchPage) |
typeof(AppsListPage), …)` bubbles all the way out to the Windows App SDK
runtime and is stowed as a XAML failure. Compare with
`src\settings-ui\Settings.UI\SettingsXAML\App.xaml.cs`, which already
wires `UnhandledException += App_UnhandledException`.

## Changes

**`src\settings-ui\QuickAccess.UI\QuickAccessXAML\App.xaml.cs`**

- Hook `Application.UnhandledException` in the constructor. The handler
logs the exception via `ManagedCommon.Logger.LogError` (same logger
Settings uses) and sets `e.Handled = true`. QuickAccess is a transient
launcher flyout owned by the runner, so swallowing a stray XAML error
and keeping the host alive for the next summon is the correct trade-off
— the failure is still recorded for diagnostics.
- Wrap the body of `OnLaunched` in a try/catch. If `MainWindow` (which
sets up window chrome, listener threads, the IPC coordinator, and the
XAML shell) fails to construct, log the exception and call `Exit()`
cleanly rather than letting the throw escape into the Windows App SDK
launch path.

**`src\settings-ui\QuickAccess.UI\QuickAccessXAML\Flyout\ShellPage.xaml.cs`**

- Subscribe to `ContentFrame.NavigationFailed` after
`InitializeComponent`. A page constructor or XAML-load failure in
`LaunchPage` / `AppsListPage` would otherwise bubble out of the `Frame`
and crash the launcher. The handler logs the failure
(`SourcePageType.FullName` + the exception) and marks it handled so the
next summon retries navigation.

No production behaviour changes when things work — only the failure
paths are different. No public API surface changes.

## Why both handlers, not just one

- `Application.UnhandledException` does not fire for
`Frame.NavigationFailed`. The Frame raises its own event first and, if
no handler runs or `e.Handled` is left `false`, then it rethrows on the
dispatcher.
- Conversely, `Frame.NavigationFailed` only fires for navigation
failures — not for an exception thrown directly in `OnLaunched` before
any navigation happens.

The two events are complementary, so both need a handler to fully cover
the launch + navigation paths.

## Testing

- The local NuGet feed on my dev box currently can't restore
`Microsoft.NETCore.App.Runtime.win-x64 = 10.0.9` (the feed only has
`11.0.0-preview.1.26104.118`), which fails the project restore for every
WinUI project including this one. That's the same environment issue I
called out on #48414 — pipeline restore uses a different feed and is
fine.
- All three patterns added here are copy-paste analogues of code that
already exists in `Settings.UI` (`App.xaml.cs:96, 106-109`,
`ShellViewModel.cs:86, 136`), so namespace and signature drift risk is
minimal. The only behavioural difference is `e.Handled = true`, which is
the actual goal of this PR.

## Risk

- Low. Two new event handlers and one try/catch. No behaviour change on
the success path.
- Worst-case regression is that a real, repeatable XAML failure becomes
silent in the runner's eyes (no process crash) instead of loud — but
it's logged via `Logger.LogError` so the user can still find the trace
in `%LOCALAPPDATA%\Microsoft\PowerToys\Logs\`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---

ADO:
https://microsoft.visualstudio.com/DefaultCollection/OS/_workitems/edit/61258633/

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit ab4947579b)
2026-06-22 16:29:51 +08:00
Matheus Mol
7279bfd9ec [KBM] Fix modifier key remapped to non-modifier delivering WM_SYSKEYDOWN (#47192)
## Summary of the Pull Request

When a modifier key (Ctrl/Alt/Shift) is remapped to a non-modifier key
using
Keyboard Manager, the injected key event is delivered to applications as
WM_SYSKEYDOWN instead of WM_KEYDOWN. This causes unexpected behavior —
for
example, remapping Left Alt to Backspace results in whole words being
deleted
instead of single characters, because applications interpret
WM_SYSKEYDOWN +
VK_BACK as Alt+Backspace.

The fix resets the modifier state with a suppress-flag key-up event
before
injecting the target key, consistent with the existing approach used for
the
Caps Lock remapping scenario.

## PR Checklist

- [ ] Closes: #47191
- [ ] Communication: I've discussed this with core contributors already.
If the work hasn't been agreed, this work might be rejected
- [x] Tests: Added/updated and all pass
- [ ] Localization: All end-user-facing strings can be localized
- [ ] Dev docs: Added/updated
- [ ] New binaries: Added on the required places

## Detailed Description of the Pull Request / Additional comments

The root cause is that SendInput is called inside the low-level keyboard
hook
callback before the original modifier event is suppressed. At that point
the
modifier state is still active, so the OS delivers the injected key as
WM_SYSKEYDOWN (system key with Alt context) rather than WM_KEYDOWN.

This is the same mechanism that was already fixed for the Ctrl/Alt/Shift
↔
Caps Lock case. This PR extends the fix to cover modifier → non-modifier
remaps.

## Validation Steps Performed

1. Remapped Left Alt → Backspace in Keyboard Manager
2. Opened a text editor, typed text, pressed the remapped key
3. Confirmed single characters are deleted instead of whole words
4. All 93 unit tests pass (KeyboardManager.Engine.UnitTests)

(cherry picked from commit 5688441127)
2026-06-22 16:29:51 +08:00
Mario Hewardt
8d6bb43c26 ZoomIt - Fix race condition in audio init (#48685)
It was a race condition.

(cherry picked from commit d9216f0fc7)
2026-06-22 16:29:51 +08:00
moooyo
b82e3535da [PowerDisplay] Allow waking a monitor from standby via power-state On (#48628)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

Power Display could put a monitor to sleep but never wake it back up.
Selecting **On** in the per-monitor power-state list was a hard-coded
no-op, so the DDC/CI wake command (VCP `0xD6` = `0x01`) was never sent.
This removes that guard so selecting **On** wakes the display, and
cleans up the dead code/comment left behind by the original
one-directional design.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #48428
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

`MonitorViewModel.HandlePowerStateSelectionChanged` early-returned when
the selected power state was **On** (`0x01`), so `SetPowerStateAsync`
was never called for On and the wake write never reached the monitor. As
a result Power Display's power control was one-directional: it could
send Standby/Suspend/Off but could never turn a monitor back on.

The guard dates back to the very first power-state commit and was paired
with a single-monitor assumption — *"the monitor must be on to see the
UI"*, so On was treated as the always-current state and skipped. A later
change made the selection reflect the monitor's real power state (so a
monitor in the list can legitimately be asleep), and multi-monitor
support means the flyout can be shown on monitor A while the user wants
to wake monitor B. Those changes invalidated the assumption, but the
action-side guard survived a subsequent refactor.

The lower layers already do the right thing:
`MonitorManager.SetPowerStateAsync` →
`DdcCiController.SetPowerStateAsync` → `SetVcpFeatureAsync(monitor,
0xD6, value)` passes the value through unchanged, so the fix is purely
removing the UI-layer guard. DDC/CI stays reachable while the panel is
in Standby/Suspend/Off(DPM), so writing `0x01` turns it back on (this is
the same mechanism Twinkle Tray uses). `Off (Hard)` / `0x05` may still
require a physical wake on some monitors, since that state can cut the
DDC command channel.

Cleanup included in this PR:
- Removed the now-unused `PowerStateItem.PowerStateOn` constant (its
only consumer was the deleted guard).
- Removed the dead `SetPowerState` `[RelayCommand]` (the generated
`SetPowerStateCommand` had zero references — the XAML wires
`SelectionChanged`, not a command).
- Updated the `SetPowerStateAsync` doc comment from the one-directional
framing to a neutral bidirectional description.

Net change: 2 files, +5 / −24.

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

- **Build:** `MSBuild PowerDisplay.csproj -p:Platform=x64
-p:Configuration=Debug` (CoreCompile) — **0 errors / 0 warnings**.
- **Static check:** repo-wide grep confirms no remaining references to
`PowerStateOn` or `SetPowerStateCommand`; the power-state ListView binds
only `ItemsSource` + `SelectionChanged` (no `SelectedItem` binding), so
opening the flyout cannot spuriously re-fire a selection.
- **Manual (requires a DDC/CI monitor):** enable *Power state control*
for a monitor → open its flyout and select **Standby** or **Off (DPM)**
(screen blanks) → reopen the flyout and select **On** → the display
wakes.

No automated test was added: with the guard removed the handler is an
unconditional pass-through (identical in shape to
`HandleInputSourceSelectionChanged`), and it is an `async void` WinUI
event handler over real DDC/CI hardware, which is outside the
`PowerDisplay.Lib` unit-test seam.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Yu Leng <yuleng@microsoft.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(cherry picked from commit ff3c1f9252)
2026-06-22 16:29:13 +08:00
Boliang Zhang (from Dev Box)
d81186267d Merge main into stable for 0.100 release (rev 14) 2026-06-09 23:45:55 +08:00
Boliang Zhang (from Dev Box)
d22589bcf6 Revert "Add copy monitor diagnostics button to Power Display (#48209)"
This reverts commit 76eb6eaac5.
2026-06-05 15:42:28 +08:00
Boliang Zhang (from Dev Box)
0aed70cc87 Merge main into stable for 0.100 release (rev 13) 2026-06-05 11:33:54 +08:00
Boliang Zhang (from Dev Box)
ba70becdef Merge main into stable for 0.100 release (rev 12) 2026-06-03 17:43:12 +08:00
Mike Griese
820d98cb43 cmdpal: fix the dock window border being visible, redux (#48180)
Addenda to #47187

that fix only works if the _hwnd is already set. Actually it's crazy it
ever worked.

Tested by disconnecting and reconnecting RDP a couple times, which
pretty consistently reproduces the problem.

(cherry picked from commit c6a9ad2ad0)
2026-06-02 21:41:29 +08:00
Dustin L. Howett
cbeeefcaf9 Remove our dependency on expected-lite (#48159)
This removes our last git submodule dependency!

We were using `expected-lite` in one place, which was being compiled out
_anyway_ in favor of using `std::expected`.

(cherry picked from commit 109c63ba33)
2026-06-02 21:41:29 +08:00
Boliang Zhang
6ceb53336f Migrate spdlog from submodule to vcpkg (#48039)
## Summary

Migrate `deps/spdlog` from a git submodule to **vcpkg manifest mode**
with an overlay port pinned to the **exact same commit**
(`gabime/spdlog@616866fc`). Replaces the polyfill shim added in #47910
with a proper port-level patch.

This is the follow-up to PR #47928, which I closed after @zadjii-msft /
@DHowett clarified that the intended direction was a single combined
"move to vcpkg **and** apply a patch file" (one change, not two stepping
stones).

## Guidance honored

Per @zadjii-msft (offline):
-  Convert each submodule to vcpkg **one at a time** — this PR is
**spdlog only**. `deps/expected-lite` stays a submodule (separate PR
next).
-  Atomic commit per dep (multiple commits on the branch for review
traceability; squash on merge gives the requested single commit).
-  **Don't bump the version.** Only variable changed: submodule →
vcpkg. Same commit (`616866fc`, v1.8.5 + 38) the submodule pointed at.

Per @DHowett
([review](https://github.com/microsoft/PowerToys/pull/48039#pullrequestreview-4338835150)):
-  No vcpkg submodule — vswhere-first detection via a Terminal-style
`steps-install-vcpkg.yml` template; three-tier `VcpkgRoot` fallback (env
var → VS-shipped → runtime clone pinned to manifest baseline).

## Design

- **Repo-root manifest**: `vcpkg.json` declares only `spdlog`, with
`builtin-baseline` pinned. `vcpkg-configuration.json` registers
`deps/vcpkg-overlays/` as overlay-ports.
- **Overlay port** `deps/vcpkg-overlays/spdlog/`: `vcpkg_from_github(REF
616866fc...)` with bundled fmt preserved (`-DSPDLOG_FMT_EXTERNAL=OFF`);
the MSVC 14.51 fix from #47910 carried as a proper vcpkg patch on
`include/spdlog/fmt/bundled/format.h`.
- **vcpkg integration is global** (set in `Cpp.Build.props`, imported
via `ForceImportBeforeCppProps` for every `.vcxproj`). An earlier
attempt to make vcpkg per-project-opt-in via `deps/spdlog.props` failed
because ~85 PowerToys `.vcxproj` files import `spdlog.props` AFTER
`Microsoft.Cpp.targets`, by which point `vcpkg.props`' `ClCompile` hook
is dead-on-arrival. The trade-off (every C++ project invokes `vcpkg
install` once at build time, ~0.5 s on cache hits, manifest declares
only spdlog so install set is fixed) is documented in the expanded
`Cpp.Build.props` comment.
- **`deps/spdlog.props`** is now a thin shim that only sets the
historical `SPDLOG_*` preprocessor defines for source-compat.
- **`Cpp.Build.targets`** is a new file imported via
`ForceImportAfterCppTargets` to load `vcpkg.targets` after
`Microsoft.Cpp.targets`. A fail-fast `<Target>` errors with a clear
message if `vcpkg.props` can't be found at the resolved `VcpkgRoot`.
- **Removes** `deps/spdlog-msvc-fix/` polyfill, in-tree wrapper
`src/logging/`, spdlog submodule, the single `<ProjectReference>` in
`logger.vcxproj`, plus 3 `.slnf` refs and 2 `.slnx` refs
(`PowerToys.slnx` + `installer/PowerToysSetup.slnx`), plus 3 hard-coded
`..\deps\spdlog\include` entries in `<AdditionalIncludeDirectories>`.
- **CI**: new reusable `.pipelines/v2/templates/steps-install-vcpkg.yml`
(vswhere-first, manifest-baseline-pinned fallback clone, respects
`useVSPreview`). Gated `Cache@2` for `%LOCALAPPDATA%\vcpkg\archives`
keyed on overlay-port contents. Same vcpkg detection added to
`tools\build\build-essentials.ps1` for local devs.

## Verification

Local build matrix (all 4 configs of `logger.vcxproj` and a
representative late-import consumer):

| Config | Result | Notes |
|--------|--------|-------|
| Release \| x64    |  | vcpkg install ~21 s, `logger.lib` produced |
| Debug \| x64 |  | **Validates patch fixes the actual MSVC 14.51 bug**
(`_ITERATOR_DEBUG_LEVEL > 0` → `_SECURE_SCL`) |
| Release \| ARM64 |  | vcpkg cross-installs `arm64-windows-static`
spdlog in ~16 s |
| Debug \| ARM64 |  | **Previously DISABLED for the in-tree spdlog**
(per `<Build Solution="Debug\|ARM64" Project="false" />` in
`PowerToysSetup.slnx`); this migration FIXES that latent gap |
| FancyZonesLib (Release \| x64) |  | Late-import-pattern consumer;
previously broke in v2 |

Full PowerToys CI (x64 + arm64 + CmdPal SDK + all GitHub Actions checks)
green.

**Consumer audit**: 72 `.vcxproj` files reference `logger.vcxproj`; all
72 also import `deps/spdlog.props`. No transitive-link breakage.

## Out of scope (intentional)

- `deps/expected-lite` migration — next PR per "one-at-a-time" rule.
- Remote vcpkg binary cache (Azure Artifacts NuGet feed). Local pipeline
`Cache@2` works for now, but a remote feed survives across pipelines and
is the long-term answer. Happy to split this into a follow-up.

## Notes for review

- Patch in the overlay port is identical content to PR #47928's patch
but regenerated with LF line endings (vcpkg's `vcpkg_apply_patches` is
strict; no `--ignore-whitespace`).
- Once PowerToys eventually bumps spdlog past v1.14 (which ships fmt
10.2 and drops the affected code path), the overlay port can be deleted
and we can use upstream vcpkg's `spdlog` directly.
- Re. official-release pipelines and terrapin / less-restricted network
isolation: VS-shipped vcpkg is the primary path (no network); the
fallback clone is only exercised when VS doesn't ship vcpkg. Happy to
wire terrapin into the fallback as a follow-up if the official build
template needs it.

Closes the work tracked in #47928 (which was closed unmerged).

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
(cherry picked from commit 8a7933c0b2)
2026-06-02 21:41:29 +08:00
Niels Laute
5812b55710 Rework Power Display warning dialog (#48249)
## Summary of the Pull Request

Rework the confirmation dialog shown when enabling Power Display (and
its potentially-destructive sub-features) so it is shorter, friendlier,
and consistent across all entry points. The five separate prefix-driven
variants are now a single `PowerDisplayWarningDialog` user control
selected via an enum, sharing the title, learn-more link, and
Enable/Cancel buttons. The previous hand-rolled red warning text is
replaced with a Fluent `InfoBar Severity=""Warning""`.

## PR Checklist

- [ ] Closes: #xxx
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places

## Detailed Description of the Pull Request / Additional comments

### Before
- `DangerousFeatureWarningDialog` took a resource-key prefix string and
probed up to five optional keys per variant (`_WarningHeader`,
`_WarningConfirm`, `_WarningList_Item1/2`, etc.).
- Each of the five flows (EnableModule, ColorTemperature, PowerState,
InputSource, MaxCompatibility) had a slightly different title (some
questions, some statements, mixed `Warning:` prefixes) and a hand-rolled
red `TextBlock` warning header with a `⚠️` emoji and
`SystemFillColorCriticalBrush`.
- ~30 fragmented `PowerDisplay_*_Warning*` resw keys.

### After
- New `PowerDisplayWarningDialog` selected via `PowerDisplayWarningKind`
enum (`EnableModule`, `ColorTemperature`, `PowerState`, `InputSource`,
`MaxCompatibility`).
- Shared chrome lives in the control:
  - Single title `Before you continue` for every variant.
  - `InfoBar Severity=""Warning""` replaces the hand-rolled red header.
- Learn-more `HyperlinkButton` pointing at
`aka.ms/powerToysOverview_PowerDisplay_Note` (URL is a `private const`
so translators don't see it).
  - Consistent Enable / Cancel buttons.
- Per-variant content collapses to one InfoBar message + one body
paragraph in resw (12 keys total, down from ~30). Bullets are inlined as
`• ` + newlines with `xml:space=""preserve""`.
- `PowerDisplayViewModel.ConfirmDangerousFeatureAsync` and
`TryCommitDangerousChangeAsync` now take the enum instead of a magic
string.

### Files
- **Added:** `ViewModels/PowerDisplayWarningKind.cs`,
`SettingsXAML/Views/PowerDisplayWarningDialog.xaml{,.cs}`
- **Removed:**
`SettingsXAML/Views/DangerousFeatureWarningDialog.xaml{,.cs}`
- **Updated:** `Strings/en-us/Resources.resw`,
`ViewModels/PowerDisplayViewModel.cs`,
`SettingsXAML/Views/PowerDisplayPage.xaml.cs`

## Validation Steps Performed

- Built `PowerToys.Settings.csproj` (Debug arm64) — clean.
- Manually exercised the EnableModule and MaxCompatibility flows;
verified the new title, InfoBar, body paragraph, learn-more link, and
Enable/Cancel button behavior.
- Verified `aka.ms/powerToysOverview_PowerDisplay_Note` opens in the
default browser.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 7f19817182)
2026-06-02 15:34:10 +08:00
Niels Laute
1be856c573 [KBM] Enable new editor by default (#48245)
## Summary

Make the new WinUI 3 Keyboard Manager editor the default by flipping
`useNewEditor` from `false` to `true` everywhere a default is supplied.

## Behavior

- **New installs / new `settings.json`** → new editor enabled
- **Upgrades from a version before the `useNewEditor` key existed** →
new editor enabled (the key is missing, so the default kicks in)
- **Users who have explicitly toggled the setting** → unchanged (we only
change the default, not stored values)
- The "Go back to classic" button in the KBM settings page is untouched

## Changes

- `src/settings-ui/Settings.UI.Library/KeyboardManagerProperties.cs` —
`UseNewEditor` property initializer now `true`
- `src/modules/keyboardmanager/dll/dllmain.cpp` — `m_useNewEditor`
member init + `GetNamedBoolean` fallback now `true`; warn-log message
updated to match
-
`src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Modules/KeyboardManagerModuleCommandProvider.cs`
— both fallback paths in `IsUseNewEditorEnabled` (file missing /
unreadable) now return `true`, so the "Open New Editor" CmdPal command
surfaces by default

## Validation

Built locally on ARM64 Debug (exit code 0):
- `src/modules/keyboardmanager/dll`
- `src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys`
- `src/settings-ui/Settings.UI.Library`

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit a33fd3c474)
2026-06-02 15:34:09 +08:00
Niels Laute
c356d65e0d Reword Shortcut Guide module and OOBE descriptions (#48248)
## Summary of the Pull Request

Reword the Shortcut Guide module description and OOBE description so
they describe the feature without referring to `your apps`. The module
description now reads as a single sentence covering Windows and the
active app; the OOBE description is shortened to one paragraph and
refers to `various applications` instead of enumerating the bundled
apps.

## PR Checklist

- [ ] Closes: #xxx
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places

## Detailed Description of the Pull Request / Additional comments

Two strings in
`src/settings-ui/Settings.UI/Strings/en-us/Resources.resw` changed:

- `ShortcutGuide.ModuleDescription` — now: `Shows an on-screen overlay
of keyboard shortcuts for Windows and the active application.`
- `Oobe_ShortcutGuide.Description` — collapsed to one sentence
describing Windows + various applications, no longer enumerating bundled
apps or mentioning future additions.

No code or behavioral changes.

## Validation Steps Performed

- Built Settings.UI (Debug arm64) – clean.
- Verified the Shortcut Guide module card in Settings UI shows the new
description and the OOBE Shortcut Guide page shows the new paragraph.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
(cherry picked from commit b712fa4d85)
2026-06-02 15:34:09 +08:00
Michael Jolley
2dd1d457eb CmdPal: Synchronize fallback title/subtitle format for consistent scoring (#48085)
## Summary

Fixes #46055 — Standardizes built-in fallback title/subtitle format so
scoring is consistent across all action fallbacks.

## Problem

`MainListPage.cs` scores fallback items by fuzzy-matching the query
against both Title and Subtitle, but with different weights:
- `nameScore = FuzzyScore(query, Title)`
- `descriptionScore = (FuzzyScore(query, Subtitle) - 4) / 2`

Fallbacks that embedded the raw query in Title got artificially higher
scores than those using Subtitle. This made ranking unpredictable.

## Fix

All "action" fallbacks now follow a consistent pattern:
- **Title** = static action description (no query text)
- **Subtitle** = raw query string (unquoted)

"Result" fallbacks (that found a specific matched item) are left
unchanged — they correctly show the matched item name in Title.

## Full Fallback Audit (example query: `notepad`)

| Fallback | Title | Subtitle | Status |
|---|---|---|---|
| WebSearch: Search | "Search the web with Edge" | `Search for notepad`
| **Changed** — was `Search for "notepad"` in subtitle |
| WebSearch: Open URL | "Open in Microsoft Edge" | `Open notepad.com` |
**Changed** — was `Open "notepad.com"` in title |
| Shell: Run | "Run" | `notepad` | Unchanged — already correct |
| Calculator | "3" (result) | `1+2` (query) | Unchanged — intentional
exception |
| Indexer: single result | "notepad.exe" | `C:\Windows\notepad.exe` |
Unchanged — result fallback |
| Indexer: multiple results | "File search" | `Search for notepad in
files` | **Changed** — was `Search for "notepad" in files` in title |
| Windows Settings: single | "Notepad settings" | `Settings > Apps` |
Unchanged — result fallback |
| Windows Settings: multiple | "Search Windows settings..." | `Search
for notepad` | **Changed** — was `Search for "notepad" in Windows
settings` in title |
| Remote Desktop: exact match | "MyPC" (connection) | "Connect to MyPC"
| Unchanged — result fallback |
| Remote Desktop: arbitrary host | "Remote Desktop" | `Connect to
notepad-host` | **Changed** — was `Connect to notepad-host` in title |
| TimeDate | "Monday, May 23" (result) | "Current date" | Unchanged —
result fallback |
| System | "Shut down" (result) | "Shuts down the computer" | Unchanged
— result fallback |
| PowerToys | "Color Picker" (static) | "Pick a color..." | Unchanged —
result fallback |

## Changes

- `FallbackExecuteSearchItem.cs` — Subtitle uses raw query instead of
`"Search for \"{query}\""` format
- `FallbackOpenURLItem.cs` — Title shows browser name (was query),
Subtitle shows raw query (was browser)
- `FallbackOpenFileItem.cs` — Multi-result: Title is static display
name, Subtitle is raw query
- `FallbackWindowsSettingsItem.cs` — Multi-result: Title is static
description, Subtitle is raw query
- `FallbackRemoteDesktopItem.cs` — Arbitrary host: Title is static
"Remote Desktop", Subtitle is raw query

---------

Co-authored-by: root <root@io.bbq>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit b66b044210)
2026-06-02 11:42:13 +08:00
Michael Jolley
bb276d3be9 CmdPal: Fix dock subtitle visibility in compact mode after async update (#48088)
## Summary

When an extension updates its `Subtitle` property asynchronously after
initial render, the `TextVisibilityStates` visual state group
transitions from `TitleOnly` → `TextVisible`. This transition sets
`SubtitleText.Visibility = Visible`, overriding the `CompactStates`
setter that had hidden it.

## Fix

Added `control.UpdateCompactState()` to `OnTextPropertyChanged` in
`DockItemControl.xaml.cs`. This re-applies the compact state after any
text property change. When `IsCompact` is `false`, `UpdateCompactState`
is a no-op — no behavior change for the non-compact path.

Fixes #47980

Co-authored-by: root <root@io.bbq>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 18919eaa40)
2026-06-02 11:42:12 +08:00
Michael Jolley
b7aca7c3fc CmdPal: Reorder dock network stats to match Task Manager order (#48098)
## Summary

Fixes #47939

The Performance Monitor dock band displayed network stats as Receive →
Send, but Task Manager shows Send → Receive. This swaps the order to
match Task Manager.

## Changes

- `PerformanceWidgetsPage.cs`: Swap `_networkUpItem` (Send) before
`_networkDownItem` (Receive) in the band items array.

Co-authored-by: root <root@io.bbq>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit e0854fbaf3)
2026-06-02 11:42:12 +08:00
Michael Jolley
8e3a88df2c CmdPal: Fix hotkey navigation when palette is showing transient dock page (#48089)
## Summary

When a keyboard shortcut opens CmdPal to an extension while the palette
is already showing a dock-launched transient page, `GoHome(false)`
cannot restore the root page — the frame's back stack is empty because
the transient dock page was never pushed on top of root. The user ends
up with only the hotkey-target page in the frame with no way to navigate
back to the main list.

## Root Cause

In `ShellPage.SummonOnUiThread()`, the hotkey-to-page branch called
`GoHome(false)` before sending `ShowWindowMessage`. But when the active
page is a transient dock page, `_currentlyTransient` is still `true` and
the frame back stack is empty, so `GoHome` can't re-establish the root
page as the frame base.

## Fix

Added `ResetToHome()` to `ShellViewModel`, mirroring the pattern already
used in `WindowHiddenMessage` handling:
1. Clears `_currentlyTransient`
2. Calls `_rootPageService.GoHome()` to reset extension state
3. Sends `PerformCommandMessage` for `_rootPage` — navigating
MainListPage into the frame as the base

In `ShellPage.SummonOnUiThread()`, the `GoHome(false)` call in the
`isPage` branch is replaced with `ViewModel.ResetToHome()`. The root
page is then cleanly in the frame before the hotkey target's
`PerformCommandMessage` navigates on top of it.

Fixes #47994

Co-authored-by: root <root@io.bbq>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit ba20da1611)
2026-06-02 11:42:12 +08:00
Niels Laute
fb028e8fdc CmdPal: Reorder Pin to Dock dialog so controls precede the preview (#48250)
## Summary of the Pull Request

Reorder the Pin to Dock dialog content so the configuration controls
(monitor selector, dock section, label options) appear at the top and
the live preview is shown below them. The user now configures the pin
first and sees the resulting preview directly underneath, instead of
staring at the preview and having to scan past it to find the controls.

<img width="515" height="440" alt="image"
src="https://github.com/user-attachments/assets/0d1d0543-2b30-48f5-a1aa-676a165870f5"
/>

## PR Checklist

- [ ] Closes: #xxx
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places

## Detailed Description of the Pull Request / Additional comments

XAML-only change to
`src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/PinToDockDialogContent.xaml`.
The new visual order inside the `ScrollViewer`/`StackPanel` is:

1. Monitor selector (still `Visibility=""Collapsed""` by default; shown
when more than one monitor is available)
2. Dock section `Segmented` (Start / Center / End)
3. Label options (`Show title` / `Show subtitle` checkboxes)
4. Divider `Rectangle`
5. Preview `Border`

No logic, bindings, `x:Name` identifiers, event handlers, or `x:Uid`
keys are changed.

## Validation Steps Performed

- Built `Microsoft.CmdPal.UI` (Debug arm64) — clean.
- Verified the Pin to Dock dialog renders with controls on top and the
preview underneath; segmented selection, label-option checkboxes, and
the multi-monitor combo still behave as before.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 35f2ed839e)
2026-06-02 11:42:12 +08:00
Michael Jolley
d980697849 CmdPal: Fix GPU index out of range crash in PerfMon widget (#48103)
## Summary

Fixes #47821

The GPU Performance Monitor widget crashes with
`IndexOutOfRangeException` on systems where GPU performance counters
fail to enumerate (common on Intel Arc and hybrid GPU configurations).
The dock band shows `???` and opening the flyout causes an error.

## Root Cause

`GPUStats.CreateGPUImageUrl()` accessed `_stats[index]` without bounds
checking. When `GetGPUPerfCounters()` finds no matching counter
instances, `_stats` remains empty but callers still pass index 0.

`GetGPUName()`, `GetGPUUsage()`, and `GetGPUTemperature()` already have
proper guards (`if (_stats.Count <= index) return ...`) — this fix adds
the same pattern to the one remaining unguarded method.

## Changes

- `GPUStats.cs`: Add bounds check to `CreateGPUImageUrl()` — return
empty string if index out of range

Co-authored-by: root <root@io.bbq>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit e20b5b9c51)
2026-06-02 11:42:12 +08:00
Mike Griese
d42b15f380 cmdpal: fix the dock window border being visible, redux (#48180)
Addenda to #47187

that fix only works if the _hwnd is already set. Actually it's crazy it
ever worked.

Tested by disconnecting and reconnecting RDP a couple times, which
pretty consistently reproduces the problem.

(cherry picked from commit c6a9ad2ad0)
2026-06-02 11:41:59 +08:00
Muyuan Li
b49f722e88 Fix ShortcutGuide v2 crash when Manifests directory is missing (#48171)
## Summary

Fixes #48170 — ShortcutGuide v2 crashes on launch when the bundled
`Manifests` directory is absent from the install path.

### Root Cause

The `Assets\ShortcutGuide\Manifests\*.yml` files were never reaching the
build output directory during the CI solution-level build (`msbuild
PowerToys.slnx /t:Build -graph`). The `CopyToOutputDirectory` metadata
on `<Content>` items does not reliably copy files to a shared
`OutputPath` in this build configuration. As a result, the WiX installer
generator found no yml files to package, and the installed product was
missing the Manifests directory entirely.

At runtime, `PowerToysShortcutsPopulator.Populate()` threw an unhandled
`FileNotFoundException` causing a crash loop.

### Fix (3 layers)

1. **Code resilience** (`Program.cs`, `PowerToysShortcutsPopulator.cs`):
- Wrap `Populate()` in try/catch so a missing manifest degrades
gracefully instead of crashing
   - Add `File.Exists` guard before `File.ReadAllText`

2. **Build output** (`ShortcutGuide.Ui.csproj`):
- Add explicit `CopyManifestsToOutputDir` MSBuild target
(`AfterTargets="Build"`) that copies yml files to
`$(OutDir)Assets\ShortcutGuide\Manifests\` — same pattern as the
existing `CopyPRIFileToOutputDir` target
- Keep `<Content Include>` with `CopyToOutputDirectory` as a fallback
for publish scenarios

3. **Installer packaging** (`generateAllFileComponents.ps1`,
`ShortcutGuide.wxs`):
   - Add `*.yml` to the file inclusion list
- Add `Generate-FileList` / `Generate-FileComponents` calls for
`ShortcutGuideManifestsFiles`
- Add WiX directory definition and `RemoveFolder` component for the
Manifests directory

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit a67fc2d9b7)
2026-06-01 16:32:35 +08:00
Jessica Dene Earley-Cha
c2ea654b3c [CmdPal] Toggle "Show details" / "Hide details" with icon in context menu (#48140)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

Converts the "Show details" context menu command into a toggle that
switches between "Show details" and "Hide details" with appropriate
icons, and fixes the icon not rendering in the context menu.

Address internal a11y bug.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

(cherry picked from commit c78f6e52a0)
2026-06-01 16:32:35 +08:00
thetsaw
80e0a4d09e Add DiskAnalyzer to third-party Run plugins list (#48106)
## Summary of the Pull Request

Adds **DiskAnalyzer** to the General plugins table in
`doc/thirdPartyRunPlugins.md`.

- **Plugin:**
[Community.PowerToys.Run.Plugin.DiskAnalyzer](https://github.com/thetsaw/PowerToys.Plugin)
- - **Author:** thetsaw
- - **Keyword:** `ds`
- - **License:** MIT
- - **Platforms:** x64 and ARM64
### What it does
Scan any folder or drive to find the largest files and subfolders, view
drive usage with visual progress bars, and navigate your filesystem all
from PowerToys Run.

## PR Checklist

- [x] Plugin has been publicly available
- [ ] - [x] MIT licensed
- [ ] - [x] Releases include x64 and ARM64 zips
- [ ] - [x] plugin.json is correctly formatted
- [ ] - [x] README includes install instructions
## Detailed Description

This is a documentation-only change adding one row to the third-party
plugins table. No source code, binaries, or build files are modified.

(cherry picked from commit 9a55209d13)
2026-05-29 12:49:14 +08:00
Copilot
8c93854bbb Rename Settings UI label from “Shortcut Guide V2” to “Shortcut Guide” (#48151)
## Summary of the Pull Request

This updates PowerToys Settings to remove the obsolete “V2” suffix from
the Shortcut Guide module name. The UI now consistently shows **Shortcut
Guide**.

## PR Checklist

- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

## Detailed Description of the Pull Request / Additional comments

- **Settings navigation label**
  - Updated `Shell_ShortcutGuide.Content` to `Shortcut Guide`.
- **Module title**
  - Updated `ShortcutGuide.ModuleTitle` to `Shortcut Guide`.
- **OOBE title**
  - Updated `Oobe_ShortcutGuide.Title` to `Shortcut Guide`.

```xml
<data name="Shell_ShortcutGuide.Content" xml:space="preserve">
  <value>Shortcut Guide</value>
</data>
```

## Validation Steps Performed

- N/A for behavior-level validation in this description (change is
limited to localized display strings).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
(cherry picked from commit 0cb6fe250b)
2026-05-29 12:49:14 +08:00
moooyo
1aba1e170c Remove "NEW" tag from Power Display and Grab and Move (#48174)
## Summary of the Pull Request
Both Power Display and Grab and Move have matured beyond their initial
release phase. This removes the "NEW" `InfoBadge` from their navigation
items in Settings, the two parent navigation groups (Windowing &
Layouts, Input / Output) that surfaced the badge when collapsed, and
clears the `IsNew` flag for Power Display in the OOBE shell.

<img width="1867" height="973" alt="image"
src="https://github.com/user-attachments/assets/533f271c-c70f-414f-a76a-43fd9ffbbd44"
/>
<img width="497" height="575" alt="image"
src="https://github.com/user-attachments/assets/fe1e97c3-c806-4f42-a836-76e042630d61"
/>
<img width="1619" height="1027" alt="image"
src="https://github.com/user-attachments/assets/f5db715b-bc69-4505-803a-18a9b2716280"
/>

## PR Checklist

- [x] Closes: #48153
- [x] **Communication:** Tracked by the linked issue
- [x] **Tests:** Markup-only change; Settings.UI builds clean with WinUI
markup compiler (no XAML errors)
- [x] **Localization:** No end-user-facing strings changed
- [ ] **Dev docs:** N/A
- [ ] **New binaries:** N/A
- [ ] **Documentation updated:** N/A

## Detailed Description of the Pull Request / Additional comments
Files touched:
- `src/settings-ui/Settings.UI/SettingsXAML/Views/ShellPage.xaml` —
removed four `<InfoBadge Style="{StaticResource NewInfoBadge}" />`
blocks on `GrabAndMoveNavigationItem`, `PowerDisplayNavigationItem`, and
on the two parent group headers `WindowingAndLayoutsNavigationItem` and
`InputOutputNavigationItem` (the parent badges existed only to surface a
NEW child when the group was collapsed; with no NEW children left in
those groups, the parent badges are now stale).
- `src/settings-ui/Settings.UI/OOBE/ViewModel/OobeShellViewModel.cs` —
flipped `(PowerToysModules.PowerDisplay, true)` to
`(PowerToysModules.PowerDisplay, false)`. Grab and Move was already
`false`.

No other modules or strings affected.

## Validation Steps Performed
- Built `src\settings-ui\Settings.UI\PowerToys.Settings.csproj`
(Release|x64) with MSBuild from VS 18 Enterprise;
`PowerToys.Settings.dll` produced with 0 errors related to this change.
WinUI markup compiler would have aborted before producing the DLL if the
XAML had syntax issues.
- Diff inspected: only the five intended deletions/edits, no collateral
changes.
- Visual run-time verification of the Settings navigation pane is
recommended before merge.

Co-authored-by: Yu Leng <yuleng@microsoft.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit c0cb9417ad)
2026-05-29 12:49:14 +08:00
moooyo
c5a18aa488 [PowerDisplay] Fix false-positive crash detection on cooperative shutdown (#48173)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

Cooperative shutdowns of `PowerDisplay.exe` — Runner's `TerminateApp`
NamedPipe message, the `Terminate` named event, tray-quit, Runner-exit
detection, and PowerToys upgrades — all call `Environment.Exit(0)`
immediately. If DDC/CI discovery is mid-flight, that path skips the
`try/finally` that owns `CrashDetectionScope`, leaving `discovery.lock`
on disk. Phase 0 at the next `PowerDisplay.exe` startup then treats this
orphan as evidence of a real crash and auto-disables the module,
surfacing the "PowerDisplay has crashed" InfoBar in Settings UI.

This PR adds an `AppDomain.ProcessExit` safety-net inside
`CrashDetectionScope`. ProcessExit fires for `Environment.Exit` but
**not** for `FailFast` / BSOD / external `TerminateProcess` — exactly
the partition we need: cooperative exit → best-effort delete the lock;
involuntary kill → leave the lock for Phase 0 to detect (original design
intent preserved).

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #48169
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
<!-- no user-facing strings changed -->
- [x] **Dev docs:** Added/updated <!-- inline XML doc on
CrashDetectionScope explains the ProcessExit partition -->
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

### Root cause

`CrashDetectionScope.Begin()` writes `discovery.lock` before DDC/CI
capability fetch and `Dispose()` deletes it when the `using` block
exits. The lock is intentionally designed to survive any code path that
cannot run user-mode cleanup (BSOD, kernel OOM, `TerminateProcess`), so
that the next `PowerDisplay.exe` start can see it and run Phase 0 (write
`crash_detected.flag`, set `enabled.PowerDisplay=false` in global
`settings.json`, signal `AutoDisablePowerDisplayEvent`).

The bug is that several **cooperative** shutdown paths route to
`Environment.Exit(0)` immediately:

| Path | Code |
|---|---|
| Runner's `TerminateApp` NamedPipe | `App.xaml.cs::OnNamedPipeMessage`
→ `Shutdown()` → `Environment.Exit(0)` |
| `Terminate` named event | `App.xaml.cs::OnLaunched` →
`RegisterEvent(..., () => Environment.Exit(0), "Terminate")` |
| Tray-quit | `TrayIconService` callback → `Environment.Exit(0)` |
| Runner-exit detection | `RunnerHelper.WaitForPowerToysRunner` callback
→ `Environment.Exit(0)` |

`Environment.Exit` calls `ExitProcess` under the hood, which terminates
all threads abruptly. Background `Task.WhenAll` doing DDC capability
fetch is killed mid-flight; the `finally` block that calls
`scope.Dispose()` never runs; `discovery.lock` orphans; Phase 0 next
time false-positives.

Concrete repro from logs:
- `15:08:42.510` lock written
- `15:08:42.79` probe monitor #1
- `15:08:46.92` probe monitor #2 (started, not finished — typical probe
takes ~5s)
- `15:08:49.03` `TerminateApp` received → `Environment.Exit(0)` → no
`Dispose` log line
- `15:10:10.03` next startup: Phase 0 sees orphan lock with `pid:17712,
startedAt:2026-05-28T07:08:42Z` → writes `crash_detected.flag` →
auto-disables

### Fix

`CrashDetectionScope.Begin()` now also subscribes to
`AppDomain.CurrentDomain.ProcessExit`. The handler does a best-effort
`File.Delete(_lockPath)` (swallowing exceptions, as required for
ProcessExit handlers). `Dispose()` unsubscribes before deleting. An
`Interlocked.Exchange` guards the race between Dispose and ProcessExit
so only one of the two performs the delete.

ProcessExit's semantics match the cooperative/involuntary partition
exactly:

| Shutdown path | ProcessExit fires? | Behavior after this PR |
|---|---|---|
| `Environment.Exit(code)` (all 4 paths above) | yes | lock deleted by
handler |
| `Environment.FailFast` | no | lock survives → Phase 0 catches it
(correct: explicit FailFast = real failure) |
| BSOD / external `TerminateProcess` / kernel OOM | no | lock survives →
Phase 0 catches it (correct: original design) |
| Discovery completes normally / throws | n/a | `try/finally` calls
`Dispose()` as before; handler unsubscribed first |

### Testability

A new `IProcessExitHook` interface abstracts the subscription so unit
tests can simulate ProcessExit without terminating the test runner.
Production code uses the default `AppDomainProcessExitHook` singleton;
tests inject a fake whose `RaiseExit()` invokes subscribed handlers
synchronously.

### Files touched

-
`src/modules/powerdisplay/PowerDisplay.Lib/Services/IProcessExitHook.cs`
*(new)* — interface + production singleton
-
`src/modules/powerdisplay/PowerDisplay.Lib/Services/CrashDetectionScope.cs`
— subscribe in `Begin`, unsubscribe in `Dispose`, add `OnProcessExit`
handler, expanded class doc
-
`src/modules/powerdisplay/PowerDisplay.Lib.UnitTests/CrashDetectionScopeTests.cs`
*(new)* — 10 unit tests

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

### Automated

10 new unit tests in `CrashDetectionScopeTests`, all passing:

```
Passed Begin_WritesLockFileAtomically
Passed Begin_SubscribesToProcessExit
Passed Dispose_UnsubscribesFromProcessExit
Passed Dispose_DeletesLockFile
Passed ProcessExitFired_BeforeDispose_DeletesLock      (core scenario)
Passed ProcessExitFired_AfterDispose_DoesNothing
Passed Dispose_AfterProcessExit_DoesNotThrow
Passed ProcessExitFired_LockFileMissing_DoesNotThrow
Passed Dispose_IsIdempotent
Passed MultipleScopes_DoNotShareState
```

Full `PowerDisplay.Lib.UnitTests` suite: **129 / 132 passing**. The 3
failures (`DetectOrphanAndDisable_RunsFullSequenceWhenOrphanPresent`,
`DetectOrphanAndDisable_HandlesUnknownVersionAsOrphan`,
`DetectOrphanAndDisable_LeavesLockIntactOnSignalFailure`) are
**pre-existing on `main`** — they fail with `REGDB_E_CLASSNOTREG` from
`Constants.AutoDisablePowerDisplayEvent()` (WinRT activation factory not
COM-registered in the test environment). Verified by stashing this PR's
changes and re-running the same 3 tests on baseline `main` — same
failures, same cause, unrelated to this change.

### Manual

1. Reproduced the original false-positive on `main`:
- Enable PowerDisplay → open Settings UI → quickly toggle PowerDisplay
off
- Observe `discovery.lock` left in
`%LOCALAPPDATA%\Microsoft\PowerToys\PowerDisplay\`
- Re-enable PowerDisplay → Phase 0 writes `crash_detected.flag` →
InfoBar appears
2. Repeated the same steps with this branch:
- Toggling PowerDisplay off cleanly deletes `discovery.lock`
(ProcessExit handler ran)
- Re-enabling PowerDisplay shows no InfoBar, no `crash_detected.flag`
created
3. BSOD path is unchanged (verified by inspecting the conditional logic
— `AppDomain.ProcessExit` does not fire for involuntary terminations;
the lock survives just as before).

---------

Co-authored-by: Yu Leng <yuleng@microsoft.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit cd5027fa1a)
2026-05-29 12:49:14 +08:00
Copilot
df019c09e6 Rename OOBE overview Learn link label to “Documentation” (#48155)
## Summary of the Pull Request

Renames the OOBE welcome/overview hyperlink label from **“Documentation
on Microsoft Learn”** to **“Documentation”** for brevity and
consistency.
Scope is limited to the localized string resource used by the OOBE
overview page.

## PR Checklist

- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

## Detailed Description of the Pull Request / Additional comments

- **Resource update (OOBE Overview)**
- Updated `Oobe_Overview_DescriptionLinkText.Text` in
`src/settings-ui/Settings.UI/Strings/en-us/Resources.resw`.

```xml
<data name="Oobe_Overview_DescriptionLinkText.Text" xml:space="preserve">
  <value>Documentation</value>
</data>
```

## Validation Steps Performed

- Confirmed the OOBE overview string key now resolves to
**“Documentation”**.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
(cherry picked from commit 7da62cdb0a)
2026-05-29 12:49:14 +08:00
Eymard Silva
d97301f06f Handle complex calculator results (#47506)
## Summary of the Pull Request

Return a friendly calculator error when Mages evaluates an expression to
a complex number instead of letting decimal conversion throw.

This fixes the PowerToys Run Calculator result for expressions such as
`sqrt(-1)` by detecting `System.Numerics.Complex` results before decimal
conversion and showing a localized error message instead.

Fixes #43937

## PR Checklist

- [x] Closes: #43937
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [x] **Dev docs:** Added/updated
- [x] **New binaries:** Added on the required places
- [x] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [x] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [x] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [x] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [x] **Documentation updated:** Not required for this bug fix.

## Detailed Description of the Pull Request / Additional comments

The Calculator plugin previously passed complex results from Mages into
`Convert.ToDecimal`, which caused an exception for expressions like
`sqrt(-1)`.

This PR updates the calculator result transformation logic to detect
`System.Numerics.Complex` and return a localized user-facing error
message: `Complex numbers are not supported`.

It also updates calculator query tests to cover both direct keyword and
global query behavior.

## Validation Steps Performed

- Added unit test coverage for `=sqrt(-1)` returning `Complex numbers
are not supported`.
- Added unit test coverage for global query `sqrt(-1)` returning no
result instead of surfacing an unhandled exception.
- Ran `git diff --check`.
- Attempted local build/test with the PowerToys build scripts, but local
validation was blocked by Visual Studio/VC tooling configuration issues
unrelated to this change: `PlatformToolsetVersion` resolves to an empty
value during restore/build.

(cherry picked from commit c46083dd8d)
2026-05-29 12:49:13 +08:00
Mike Griese
86ca7c5661 Move CmdPal API spec back to cmdpal/ directory (#48160)
Reverts 0819a62 / #46926

The cmdpal API is literally generated from this spec document. It needs
to live with the rest of the code to work correctly.

Docs for authoring cmdpal extensions are on
https://learn.microsoft.com/en-us/windows/powertoys/command-palette/extension-development,
and we should direct docs commentary there.

(cherry picked from commit 65112a7b05)
2026-05-29 12:49:13 +08:00
🄂ʏᴇᴅ 🄰ʙᴅᴜʟ 🄰ᴍᴀ🄝 ✧
e37d1d5d04 Fix project template settings heading (#48148)
## Summary
- Fix a grammar typo in the PowerToy project template README heading.
- Change "Settings Informations" to "Settings Information".

## Validation
- Ran `git diff --check`.

(cherry picked from commit 6be6509c46)
2026-05-29 12:48:44 +08:00
Boliang Zhang (from Dev Box)
ac6aa80401 Merge main into stable for 0.100 release (rev 6) 2026-05-26 15:19:20 +08:00
Boliang Zhang (from Dev Box)
c4a83be733 Merge main into stable for 0.100 release (rev 5) 2026-05-26 11:28:54 +08:00
Boliang Zhang (from Dev Box)
34d01e998c Merge main into stable for 0.100 release (rev 4) 2026-05-25 13:03:13 +08:00
Boliang Zhang (from Dev Box)
b1adc8548f Merge main into stable for 0.100 release (rev 3) 2026-05-23 00:06:56 +08:00
Boliang Zhang (from Dev Box)
0e71f616de Merge main into stable for 0.100 release (rev 2) 2026-05-22 14:04:49 +08:00
Boliang Zhang (from Dev Box)
4f0f614f67 Merge main into stable for 0.100 release 2026-05-21 13:58:32 +08:00
Boliang Zhang (from Dev Box)
ddf9815613 Merge remote-tracking branch 'origin/main' into stable 2026-05-19 10:55:40 +08:00
Boliang Zhang (from Dev Box)
184ccb75ec Merge origin/main into stable 2026-04-29 13:38:21 +08:00
Boliang Zhang (from Dev Box)
455dc52430 Merge origin/main into stable 2026-04-26 21:05:06 +08:00
Boliang Zhang
ed1d15f9a1 Fix: Install CommandPalette.Extensions.winmd to WinUI3Apps for COM marshalling (#47210)
## Summary

Follow-up fix for #47177. Installs `CommandPalette.Extensions.winmd` to
the `WinUI3Apps\` directory (ExternalLocation) instead of the root
install folder.

## Problem

PR #47177 moved the sparse package's `ExternalLocation` from root to
`WinUI3Apps\`, but the `CommandPalette.Extensions.winmd` was still
installed to root via `DirectoryRef=INSTALLFOLDER`. The WinRT runtime
needs this winmd in the ExternalLocation directory for COM proxy/stub
creation during cross-process CmdPal extension activation. Without it,
`CoCreateInstance` returns `E_NOINTERFACE` and the PowerToys extension
fails to load in Command Palette.

## Fix

Split the `BaseApplications.wxs` `DirectoryRef` into two blocks:
- **winmd component** -> `WinUI3AppsInstallFolder` (for WinRT COM
marshalling)
- **auto-generated components** -> `INSTALLFOLDER` (unchanged, avoids
ICE30 conflict with `WinUI3ApplicationsFiles`)

## Validation

- [x] Local WiX compilation: no ICE30 errors
- [x] ADO CI build 145429943 (v0.99.1): passed
- [x] Manual verification on 25H2: CmdPal loads 55 commands after winmd
placed in WinUI3Apps

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-25 23:48:22 +08:00
Boliang Zhang (from Dev Box)
31da4fa112 Merge origin/main into stable 2026-04-24 22:38:59 +08:00
Boliang Zhang (from Dev Box)
3ee7518850 Merge origin/main into stable 2026-04-24 22:34:19 +08:00
Boliang Zhang (from Dev Box)
1bfa42b270 Merge origin/main into stable 2026-04-24 14:15:50 +08:00
Boliang Zhang (from Dev Box)
e0979fce59 Merge origin/main into stable 2026-04-24 10:59:26 +08:00
Boliang Zhang (from Dev Box)
b2684f74f5 Merge origin/main into stable 2026-04-24 10:55:14 +08:00
Muyuan Li
b67e11d000 Fix CmdPal crash when typing in search box (#47148)
Add reentrancy guard for FilteredItems ObservableCollection mutations.

WinUI3's native XAML renderer can pump the message loop while processing
a CollectionChanged notification from InPlaceUpdateList. This allows a
second DoOnUiThread task to begin mutating FilteredItems while the first
is still mid-update, causing heap corruption and an access violation
(0xc0000005) in ntdll.dll.

The fix introduces RunFilteredItemsUpdate() which uses a boolean flag to
detect same-thread reentrancy (C# lock is reentrant so _listLock cannot
prevent this). When reentrancy is detected, only the latest pending
update is stored and executed after the in-flight mutation completes,
ensuring the UI converges to the newest state without overlapping
mutations.

Fixes: 100% reproducible crash in CmdPal when typing any character in
the search box (build ID 145015494).

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #47145
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 17:19:11 +08:00
Boliang Zhang (from Dev Box)
a5bc91028f Merge remote-tracking branch 'origin/main' into stable 2026-04-23 16:18:06 +08:00
Boliang Zhang (from Dev Box)
1eebd47af0 Merge remote-tracking branch 'origin/main' into stable 2026-04-22 17:18:11 +08:00
Boliang Zhang (from Dev Box)
1f840000f5 Merge remote-tracking branch 'origin/main' into stable 2026-04-21 14:29:24 +08:00
Boliang Zhang (from Dev Box)
04fe688e7a Merge remote-tracking branch 'origin/main' into stable 2026-04-20 16:34:06 +08:00
Boliang Zhang (from Dev Box)
8fc31de489 Merge main into stable for 0.99 release 2026-04-16 15:36:07 +08:00
Boliang Zhang (from Dev Box)
0a510119c8 Remove duplicate PowerDisplay model classes after merge from main
The merge from main added a PowerDisplay.Models project reference to
Settings.UI.Library, but the old duplicate model classes (CustomVcpValueMapping
and ColorPresetItem) in Settings.UI.Library/Models/ were not removed, causing
CS0104 ambiguous reference errors. Additionally, two files had stale using
aliases and a XAML DataTemplate referenced the old namespace.

Changes:
- Delete Settings.UI.Library/Models/CustomVcpValueMapping.cs (duplicate)
- Delete Settings.UI.Library/Models/ColorPresetItem.cs (duplicate)
- Update PowerDisplayPage.xaml.cs and PowerDisplayViewModel.cs using directives
- Update PowerDisplayPage.xaml x:DataType to use pdmodels namespace

Verified with local build of both Settings.UI and PowerDisplay projects.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-14 11:08:53 +08:00
Boliang Zhang (from Dev Box)
38dfc55a2d Merge branch 'main' into stable
# Conflicts:
#	src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandItemViewModel.cs
#	src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/MainListPage.cs
#	src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockControl.xaml.cs
#	src/modules/cmdpal/Microsoft.CmdPal.UI/Dock/DockWindow.xaml.cs
#	src/modules/keyboardmanager/common/Helpers.cpp
#	src/settings-ui/Settings.UI.Library/MonitorInfo.cs
#	src/settings-ui/Settings.UI.Library/PowerDisplayProperties.cs
#	src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj
#	src/settings-ui/Settings.UI.Library/SettingsSerializationContext.cs
#	src/settings-ui/Settings.UI/SettingsXAML/Views/CustomVcpMappingEditorDialog.xaml.cs
2026-04-13 16:33:34 +08:00
Niels Laute
89b2dfe9ac [KBM] Manual key selection — code review fixes (#46377)
Addresses code review feedback on the KBM manual key selection feature.
No new user-facing behavior; all changes are correctness, robustness,
and maintainability fixes.

## Summary of the Pull Request

- **Localization:** All hard-coded `RemappingDialog.Title` assignments
replaced with `ResourceHelper.GetString()`; added
`RemappingDialog_TitleEdit` resource key
- **VK_DISABLED centralization:** Replaced scattered `0x100`/`"256"`
literals and local `const string vkDisabledCode` with `private const int
VkDisabled = 0x100` / `private const string VkDisabledString = "256"` on
`MainPage`
- **Disable action validation:** Added
`ValidationHelper.ValidateDisableMapping()` — same trigger-key rules as
other action types (empty keys, modifier-only, illegal shortcuts,
duplicates, conflicting modifier variants); wired into
`ValidateMapping()` switch
- **Binding-safe dropdown revert:** `TriggerKeyDropDown_KeyChanged` /
`ActionKeyDropDown_KeyChanged` no longer set `dropDown.KeyName =
e.OldKeyName` on failure (breaks `{Binding}` expression); now use
`RevertKeySelection(keys, index)` which does
`ObservableCollection.RemoveAt` + `Insert` to force a binding-tracked
refresh without touching the DP directly. `NewKeyCode == 0` ("None") is
rejected via the same path
- **Dropdown validation:** `ValidateDropDownSelection` skips
`string.IsNullOrEmpty` placeholder slots (added by
`HandleAutoGrowShrink`) when checking repeated-modifier and max-size
rules
- **`SetActionType`:** Replaced hard-coded `SelectedIndex` with
tag-matching iteration over `ActionTypeComboBox.Items`; immune to XAML
item reorder
- **`ServiceStatusHelper`:** Dispose all `Process` objects returned by
`GetProcessesByName` before returning; prevents handle accumulation on
the 3-second polling timer
- **`KeyDropDownButton.GetKeyList()`:** Filter out `KeyCode == 0`
entries (native "None" sentinel for shortcut lists) before caching
- **`SettingsManager`:** `_mappingService!` used consistently in
`CreateSettingsFromKeyboardManagerService`
- **`KeyboardHookHelper`:** Constructor catch broadened from
`DllNotFoundException or InvalidOperationException` to `Exception`

## PR Checklist

- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

## Detailed Description of the Pull Request / Additional comments

The `RevertKeySelection` pattern: in WinUI, assigning directly to a
bound `DependencyProperty` overwrites the binding expression. Using
`ObservableCollection.RemoveAt` + `Insert` instead raises
`CollectionChanged(Replace)`, causing the binding to re-read from the
source without clearing the expression.

```csharp
private static void RevertKeySelection(ObservableCollection<string> keys, int index)
{
    string current = keys[index];
    keys.RemoveAt(index);
    keys.Insert(index, current);
}
```

## Validation Steps Performed

Manually verified: dropdown key selection and revert on invalid
selection, Disable mapping save/load with the new validation, "None"
absent from key picker flyout, `SetActionType` correctly selects items
with preserved XAML order.

---------

Co-authored-by: Zach Teutsch <88554871+zateutsch@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-04-09 13:10:05 -04:00
Jaylyn Barbee
2ef65e7d63 [KBM] Fixes to text replacement issues (#46794)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This PR attempts to fix some of the issues that were introduced in
0.98.0 with text replacement

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #46498
- [x] Closes: #46440
- [x] Closes: #46366

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
In 0.98.0 I made a change to support multiline text replacement using
Ctrl + V. This was very inconsistent so I have reverted back to
_basically_ the same approach we were using before except now when we
encounter a newline indicator we send "Shift + Enter" so that this works
in chat boxes and plan editors.

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Manual testing with single line and multiline replacements in Teams,
Terminal, Git bash, Edge, etc
2026-04-09 13:10:05 -04:00
Zach Teutsch
3dc5b3a3ed [Keyboard Manager] Remove service enable/disable separate from module, fix editor clear shortcut (#46530)
Two changes to shortcuts here:
1) Remove toggling the KBM service with a shortcut or via command
palette
2) Ensure that shortcut is disabled for editor when shortcut is cleared
2026-03-26 13:35:11 -04:00
Zach Teutsch
fc5b65c5c3 [Keyboard Manager] Allow whitespace-only TextRemappings (#46510)
Title.

Closes #46453
2026-03-26 13:35:11 -04:00
Zach Teutsch
da5448b169 fix merge mixup for hotfix 0.98.1 2026-03-24 22:17:10 -04:00
Zach Teutsch
1ab685ee07 Merge 0.98.1 hotfixes into stable
Cherry-picked commits:
- Make KBM Editor pinnable (#46482)
- CmdPal: Fix missing primary context command for late-bound items (#46131)
- CmdPal: Ensure DockWindow property cleans up after itself (#46303)
- CmdPal: Hotfix commonCallbacks array initial count to prevent negative number (#46215)
- CmdPal: Fix missing app context menu actions on the main page (#46293)
- CmdPal: Fix dock popup XamlRoot handling on DockControl (#46305)
- CmdPal: Reduce DockWindow backdrop switching and visual artifacts (#46309)
- Always On Top: The opacity should be able to configure the hotkey individually (#46410)
- [OOBE] Ensure the Settings button on the SCOOBE page opens Home, not a blank page (#46203)
- CmdPal: Fix scroller scrolling and down glyph (#46447)
- [Settings] Decouple Settings.UI.Library from PowerDisplay.Lib to fix (#46325)
2026-03-24 21:25:13 -04:00
Zach Teutsch
cf137ccbbc Merge branch 'main' into stable 2026-03-16 21:39:04 -04:00
Zach Teutsch
2cc051ee33 Merge branch 'main' into stable 2026-03-12 14:29:12 -04:00
Zach Teutsch
efc89b01ff Merge branch 'main' into stable 2026-03-10 22:42:57 -04:00
Zach Teutsch
9717eaac4c Merge branch 'main' into stable 2026-03-09 22:19:50 -04:00
Zach Teutsch
b5716d0499 add kbm ui dll to esrp signing json 2026-03-04 21:58:03 -05:00
Zach Teutsch
a4d23b7607 fix ESRP path for kbm winui 2026-03-04 20:24:20 -05:00
Zach Teutsch
5409b1b907 fix AppListItem.cs dupe function from merge 2026-03-04 18:12:01 -05:00
Zach Teutsch
fbe952715c Merge branch 'main' into stable 2026-03-04 17:05:50 -05:00
Shawn Yuan
034759f949 Fix WinuiEx crash issue (#45443)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Fixes a crash related to `IsShownInSwitchers` when explorer.exe is not
running. The property has been removed from XAML and is now set in the
C# backend with added exception handling to improve stability. No
changes were made for projects where the property is set to true, as
they are not affected.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: vanzue <vanzue@outlook.com>
2026-02-06 17:31:07 +08:00
Thanh Nguyen
934c3bbce9 Fix CursorWrap "Automatically activate on utility startup" setting not persisting (#45210)
## Summary of the Pull Request

Fixes #45185 - CursorWrap "Automatically activate on utility startup"
setting cannot be disabled, and prevents spurious activation on startup.

## PR Checklist

- [x] Closes: #45185
- [x] **Communication:** Issue was reported by community; fix follows
established patterns from MousePointerCrosshairs
- [x] **Tests:** Manual validation performed by contributor (video
available)
- [x] **Localization:** No new user-facing strings added
- [ ] **Dev docs:** N/A - bug fix only
- [ ] **New binaries:** N/A - no new binaries
- [ ] **Documentation updated:** N/A - bug fix only

## Detailed Description of the Pull Request / Additional comments

### Problem

Users reported that disabling the "Automatically activate on utility
startup" setting for CursorWrap does not work - the mouse hook always
starts automatically regardless of the setting value.

### Root Causes

1. **`dllmain.cpp` `enable()` method**: `StartMouseHook()` was always
called unconditionally, ignoring `m_autoActivate`.
2. **`MouseUtilsViewModel.cs` `IsCursorWrapEnabled` setter**: enabling
CursorWrap forced `AutoActivate = true`, overriding the user's
preference.
3. **Startup edge case**: the trigger event could remain signaled from a
previous session, immediately toggling CursorWrap on startup even when
AutoActivate is off.

### Solution

1. **`src/modules/MouseUtils/CursorWrap/dllmain.cpp`**: only start the
mouse hook if `m_autoActivate` is true.
2. **`src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs`**:
remove the line that forced `AutoActivate = true` when enabling
CursorWrap.
3. **`src/modules/MouseUtils/CursorWrap/dllmain.cpp`**: reset the
trigger event on enable to avoid immediate activation on startup.

### Pattern Reference

This fix follows the same pattern used by **MousePointerCrosshairs**
module which has a similar `AutoActivate` setting that works correctly.

## Validation Steps Performed

### Build

- `tools\build\build.ps1 -Platform x64 -Configuration Debug`

### Manual validation (contributor)

#### Test Case 1: AutoActivate = false (should NOT auto-start mouse
hook)

1. Open PowerToys Settings → Mouse Utilities → Cursor Wrap
2. Enable Cursor Wrap
3. **Disable** "Automatically activate on utility startup"
4. Close PowerToys completely (right-click tray icon → Exit)
5. Restart PowerToys
6. **Expected Result**: CursorWrap module is loaded but mouse hook is
NOT active - cursor does NOT wrap at screen edges
7. Press activation hotkey (default: `Win+Alt+U`)
8. **Expected Result**: Mouse hook activates, cursor now wraps at screen
edges
9. **Actual Result**:  Works as expected

#### Test Case 2: AutoActivate = true (should auto-start mouse hook)

1. Open PowerToys Settings → Mouse Utilities → Cursor Wrap
2. Enable Cursor Wrap
3. **Enable** "Automatically activate on utility startup"
4. Close PowerToys completely
5. Restart PowerToys
6. **Expected Result**: Mouse hook is immediately active, cursor wraps
at screen edges without pressing hotkey
7. **Actual Result**:  Works as expected

#### Test Case 3: Setting persistence after restart

1. Set AutoActivate = false, restart PowerToys
2. Open Settings and verify AutoActivate is still false
3. Set AutoActivate = true, restart PowerToys
4. Open Settings and verify AutoActivate is still true
5. **Actual Result**:  Setting persists correctly

#### Test Case 4: Hotkey toggle works correctly

1. With AutoActivate = false, restart PowerToys
2. Press hotkey → cursor should start wrapping
3. Press hotkey again → cursor should stop wrapping
4. **Actual Result**:  Hotkey toggle works correctly

---

**Note**: Video demonstration available from contributor.
2026-02-05 20:34:15 +08:00
Mike Hall
ac548297c9 Add option to disable CursorWrap when on a single monitor. (#45303)
## Summary of the Pull Request
CursorWrap wraps on the outer edge of monitors, if a user is swapping
between a laptop and docked laptop with external monitors the user might
want to only enable wrapping when connected to external monitors, and
disable when only on the laptop.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #45198
- [ ] Closes: #45154
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

## Detailed Description of the Pull Request / Additional comments
Currently CursorWrap will wrap around the horizontal/vertical edges of
monitors, if the user has more than one monitor the outer edges are used
as wrap targets, if the user only has one monitor (perhaps a laptop)
wrapping might be temporarily disabled until additional external
monitors are added (such as being plugged into a dock or using a USB-C
monitor).

The new option will disable wrapping if only a single monitor is
detected, monitor detection is dynamic.

## Validation Steps Performed
Validated on a Surface Laptop 7 Pro (Intel) with a USB-C External
Monitor.

---------

Co-authored-by: Niels Laute <niels.laute@live.nl>
2026-02-05 20:33:49 +08:00
Shawn Yuan
17a215d321 Fix Advanced Paste settings page crash issue (#45207)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This pull request refactors the `AdvancedPasteAdditionalActions` class
to use private backing fields and custom property accessors for its
action properties. This change allows for better control over property
initialization and ensures that the properties always have valid,
non-null default values.

**Refactoring for property initialization and null safety:**

* Introduced private backing fields (`_imageToText`, `_pasteAsFile`,
`_transcode`) for the `ImageToText`, `PasteAsFile`, and `Transcode`
properties in `AdvancedPasteAdditionalActions`, replacing
auto-properties.
* Updated the property accessors for `ImageToText`, `PasteAsFile`, and
`Transcode` to use the new backing fields and ensure that a new default
instance is assigned if a null value is provided during initialization.
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #45189
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2026-02-05 10:37:31 +08:00
Jaylyn Barbee
1b6a8c54ff [Light Switch] Fix Light Switch start up logic (#45304)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Title

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: https://github.com/microsoft/PowerToys/issues/45291
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
Before, there was a function that initialized some variables about the
current system state that were later used to check against if that state
needed to change in a different function. That caused from some issues
because I was reusing the function for a double purpose. Now the
`SyncInitialThemeState()` function in the State Manager will sync those
initial variables and apply the correct theme if needed.

I also removed an unnecessary parameter from `onTick`

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Manual testing
2026-02-05 10:29:42 +08:00
Kai Tao
67518dd754 Workspace: Fix an overlay issue for workspace snapshot draw (#45183)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Root cause: Workspaces uses DPI-unaware coordinates (via
GetDpiUnawareScreens()
which runs in a temporary DPI-unaware thread) to store/match window
positions
across different DPI settings. However, WorkspacesEditor itself uses
PerMonitorV2
DPI awareness for UI clarity. When assigning these DPI-unaware
coordinates directly
to WPF window properties, WPF automatically scaled them again based on
current DPI,
causing incorrect overlay positioning.

Fix: Use SetWindowPositionDpiUnaware() to bypass WPF's automatic DPI
scaling
by temporarily switching to DPI-unaware context when calling Win32
SetWindowPos.

Fix #45174

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #45174
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Verified in local build vs production build, and the problem fixed in
local build.
2026-02-05 10:29:36 +08:00
Kai Tao
18d1fd568c PowerToys extension: Bundle localization files into installer (#45194)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #45171
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
<img width="925" height="612" alt="image"
src="https://github.com/user-attachments/assets/214ead95-504a-4e48-bc25-138323d973f9"
/>
2026-02-05 10:29:31 +08:00
leileizhang
3eae35f356 [ImageResizer] Fix Image Resizer not working after upgrade on Windows 10 (#45184)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
- Fixes an issue where Image Resizer stops working after upgrading
PowerToys on Windows 10
- Root cause: the PackageIdentityMSIX (sparse app) was not being
properly cleaned up during upgrade

## Problem
Previous versions of PowerToys installed the sparse app on Windows 10.
The current version only installs it on Windows 11+ (build >= 22000).
During upgrade on Windows 10:
1. The `NOT UPGRADINGPRODUCTCODE` condition prevented the uninstall
action from running
2. The Windows 11 version check prevented the new sparse app from being
installed
3. Result: the old sparse app remained on the system, causing Image
Resizer to malfunction

## Fix
Changed the `UninstallPackageIdentityMSIX` condition from:
Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
to:
Installed AND (REMOVE="ALL")

This ensures the old sparse app is properly cleaned up during upgrades,
which is also consistent with other similar cleanup

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #45178 #45280
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
1. Install PowerToys version 0.96.1 on Windows 10.
2. Upgrade to version 0.97.1.
3. Run Get-AppxPackage -Name "*Sparse*" in PowerShell to check whether a
Sparse App package is present.
2026-02-05 10:29:25 +08:00
Niels Laute
e349779766 Fix contrast issue (#45367)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [X] Closes: #42261
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2026-02-05 10:29:19 +08:00
Mike Hall
5466ab6cf8 CursorWrap improvements (#44936)
## Summary of the Pull Request
- Updated engine for better multi-monitor support.
- Closing the laptop lid will now update the monitor topology
- New settings/dropdown to support wrapping on horizontal, vertical, or
both

<img width="1103" height="643" alt="image"
src="https://github.com/user-attachments/assets/ff4f0835-a8ca-4603-9441-123b71747d5c"
/>

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #44820
- [x] Closes: #44864
- [x] Closes: #44952

- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

## Detailed Description of the Pull Request / Additional comments
Feedback for CursorWrap shows that users want the ability to constrain
wrapping for horizontal only, vertical only, or both (default behavior).
This PR adds a new dropdown to CursorWrap settings to enable a user to
select the appropriate wrapping model.

## Validation Steps Performed
Local build and running on Surface Laptop 7 Pro - will also validate on
a multi-monitor setup.

---------

Co-authored-by: vanzue <vanzue@outlook.com>
2026-01-27 13:28:31 +08:00
Heiko
bf19bdc1ee [Enterprise; Policy] Add policy for CursorWrap to ADMX (#45028)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

Added missing policy definition.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #44897
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [x] **Documentation updated:** See PR for issue #44484 

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2026-01-27 11:42:06 +08:00
Jiří Polášek
2441621b80 CmdPal: Remove deadlock bait from AppListItem (#45076)
## Summary of the Pull Request

This PR removes a Task.Wait() call from lazy-loading AppListItem details
that could be invoked on the UI thread and lead to a deadlock.

It now follows the same pattern previously used for loading icons in the
same class, which has proven to work well.

Prevents #44938 from stepping on this landmine.

Cherry-picked from #44973.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #45074 
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2026-01-27 10:36:42 +08:00
Shawn Yuan
1ca9d10ff5 [Settings] [Advanced Paste] Upgrade advanced paste settings safely to fix settings ui crash (#44862)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This pull request makes a minor fix in the `AdvancedPasteViewModel`
constructor to ensure the correct settings repository is used for null
checking. The change improves code correctness by verifying
`advancedPasteSettingsRepository` instead of the generic
`settingsRepository`.

- Fixed null check to use `advancedPasteSettingsRepository` instead of
`settingsRepository` in the `AdvancedPasteViewModel` constructor for
more accurate validation.
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #44835
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2026-01-26 16:06:19 +08:00
Gordon Lam
b438f15f6e [Peek] Fix Space key triggering during file rename (#44845) (#44995)
Don't show error window when CurrentItem is null - just return silently.
This restores the original behavior where CaretVisible() detection in
GetSelectedItems() would suppress Peek by returning null, and no window
would be shown.

PR #44703 added an error window for virtual folders (Home/Recent), but
this also triggered when user was typing (rename, search, address bar),
stealing focus and cancelling the operation.

Fixes #44845

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
2026-01-26 16:06:12 +08:00
Shawn Yuan
48de981f50 Add telemetry for tray icon (#44985)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This pull request adds telemetry tracking for user interactions with the
application's tray icon. Specifically, it introduces new methods for
logging `left-click`, `right-click`, and `double-click` events, and
integrates these telemetry calls into the tray icon event handling
logic.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2026-01-26 16:06:06 +08:00
Shawn Yuan
9ab6559fac [Settings] Fix right click menu display issue (#44982)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This pull request updates the tray icon context menu logic to better
reflect the state of the "Quick Access" feature. The menu now
dynamically updates its items and labels based on whether Quick Access
is enabled or disabled, improving clarity for users.

**Menu behavior improvements:**

* The tray icon menu now reloads itself when the Quick Access setting
changes, ensuring the menu always matches the current state.
* The "Settings" menu item label changes to "Settings\tLeft-click" when
Quick Access is disabled, providing clearer instructions to users.
[[1]](diffhunk://#diff-e5efbda4c356e159a6ca82a425db84438ab4014d1d90377b98a2eb6d9632d32dR176-R179)
[[2]](diffhunk://#diff-7139ecb2cf76e472c574a155268c19e919e2cce05d9d345c50c1f1bffc939e1aR198-R248)
* The Quick Access menu item is removed from the context menu when the
feature is disabled, preventing confusion.

**Internal state tracking:**

* Added a new variable `last_quick_access_state` to track the previous
Quick Access state and trigger menu reloads only when necessary.
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #44810
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
- When Quick Access is disabled

<img width="1537" height="312" alt="image"
src="https://github.com/user-attachments/assets/5d51f24e-ccb4-4973-afaa-8b64cc35db87"
/>

- When Quick Access is enabled
<img width="1601" height="201" alt="image"
src="https://github.com/user-attachments/assets/56366d10-bcec-4892-b2d2-f8213ad726aa"
/>

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2026-01-26 16:05:58 +08:00
moooyo
8cc32d3098 fix: Improve Unicode normalization and add regex metachar tests (#44944)
Enhanced SanitizeAndNormalize to handle Unicode normalization more
robustly, ensuring correct buffer sizing and error handling. Added unit
tests for regex metacharacters `$` and `^` to verify correct replacement
behavior at string boundaries. Improves Unicode support and test
coverage for regex edge cases.

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #44942 #44892
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: Yu Leng <yuleng@microsoft.com>
2026-01-26 16:05:54 +08:00
Jiří Polášek
3b7eedfb67 CmdPal: Improve loading of application icons - part 1 (#44938)
## Summary of the Pull Request

This PR improves loading of application icons:

- Fixes loading of icons from internet shortcuts

## Pictures? Pictures!

<img width="683" height="399" alt="image"
src="https://github.com/user-attachments/assets/5e566648-7b1a-4254-8afd-557a321b19d6"
/>


<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #44819
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2026-01-26 16:05:49 +08:00
Kai Tao
d7e1b18ba4 Runner TrayIcon: Monochrome icon should adapt to windows theme instead of the app theme (#44931)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
As title
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [X] Closes: #44891
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
============ System light + App Light
<img width="903" height="239" alt="image"
src="https://github.com/user-attachments/assets/581606fb-99b5-4df9-a520-545a0c04676c"
/>
============ System Light + App Dark
<img width="991" height="239" alt="image"
src="https://github.com/user-attachments/assets/822009e9-57cf-452b-b3aa-f1cbc25883f8"
/>
============ System Dark + App Light
<img width="932" height="236" alt="image"
src="https://github.com/user-attachments/assets/98a56d48-31f0-4f75-95a4-8c7dc83c3866"
/>
============ System Dark + App Dark
<img width="903" height="236" alt="image"
src="https://github.com/user-attachments/assets/2500a0d5-6b27-403e-89b4-69b7d3b91e79"
/>
============
2026-01-26 16:05:43 +08:00
Kai Tao
0206fdbec1 Cmdpal: use latest msix to install (#44886)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
We should install latest cmdpal msix
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #44860
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2026-01-26 16:05:36 +08:00
Leilei Zhang
bdaf644f02 test sign 2026-01-19 14:29:46 +08:00
moooyo
329c8c2616 refactor(imageresizer): disable AI feature and cache functionality (#44759)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Co-authored-by: Yu Leng <yuleng@microsoft.com>
2026-01-19 10:51:10 +08:00
Niels Laute
b081e413b1 Upgrade MarkdownTextBlock (#44793)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

This PR:

- upgrades `CommunityToolkit.WinUI.Labs.MarkdownTextBlock` to version
`0.1.260116-build.2514`. This update includes a bunch of improvements
for markdown rendering in its default config, and fixes a couple of bug
with regards to rendering large images getting clipped when resizing the
window.
- replaces an incorrect image in the `Command Palette Sample Page`
extension for the markdown + images sample.

Before vs after:

<img width="910" height="234" alt="image"
src="https://github.com/user-attachments/assets/b3dad76c-a89e-4b47-90f8-d3c64f00615f"
/>


<img width="1245" height="827" alt="image"
src="https://github.com/user-attachments/assets/00037fb5-453e-4d85-83c9-92c265b9f968"
/>



<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2026-01-19 09:56:30 +08:00
leileizhang
290fa01adf [ImageResizer] Temporarily disable AI Super Resolution feature (#44768)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Temporarily disables the AI Super Resolution feature in Image Resizer
while keeping all code intact for re-enabling in a future release.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2026-01-17 11:28:47 +08:00
282 changed files with 2678 additions and 302347 deletions

View File

@@ -432,6 +432,3 @@ SHELLEXPERIENCEHOST
SHELLHOST
STARTMENUEXPERIENCEHOST
WIDGETBOARD
# URIs
actioncenter

View File

@@ -1,51 +1,23 @@
accelscroll
acq
ADDTO
ADDTOOL
adr
Adr
ALWAYSTIP
APPLYTOSUBMENUS
ARCHMASK
archs
AUDCLNT
autocorr
avx
axisdefer
axisflip
axisstart
backlight
BEOS
bfi
BFIN
bfly
BGRX
bitmaps
bitrev
blits
Borgerding
Borland
breakc
BREAKSCR
BUFFERFLAGS
bugzilla
Cands
capturepath
cbs
centiseconds
cexp
cfx
cfy
cgem
cifx
cify
CLASSW
coeffs
colblocks
constantbuffer
coprime
cpuid
cpx
CREATEDIBSECTION
CREATESTRUCTW
crossfades
@@ -56,216 +28,109 @@ CTLCOLORDLG
CTLCOLOREDIT
CTLCOLORLISTBOX
CTrim
CVTEPI
DBuffer
dcl
dct
ddx
ddy
Deinterleave
denoise
denoised
DEVSOURCE
DFCS
DIVSCALAR
DJGPP
dlg
dlu
dnn
DONTCARE
downsample
DRAWITEM
DRAWITEMSTRUCT
droppedband
Droppedband
DSPs
dsum
dupburst
dupsegments
DWLP
eband
ebx
ECX
EDITCONTROL
EDSP
emmintrin
EMX
ENABLEHOOK
endloop
ENDOFSTREAM
ener
enh
ettings
expectedlock
expf
fabs
fabsf
facbuf
fastscroll
FDE
ffast
FIXDIV
floorf
fmadd
fout
fstride
fxc
GETCHANNELRECT
GETCHECK
GETCOUNT
GETDISPINFO
GETSCREENSAVEACTIVE
GETSCREENSAVETIMEOUT
GETTHUMBRECT
GIFs
glu
groupshared
gru
hcfdark
hcfwhitespace
hlsl
Hsieh
hstride
HTBOTTOMRIGHT
HTHEME
htol
ICONINFORMATION
ICONWARNING
idct
IDIn
IDISHWND
ifft
igc
ilog
imad
imax
imin
immintrin
Inj
interp
inttypes
ishl
itof
jumprecover
kfft
kheight
kissfft
KSDATAFORMAT
ksize
ktime
lastg
latestcapture
ldx
LEFTNOWORDWRAP
legitjumps
lenmem
letterbox
lld
lldx
llu
llums
logfont
lookback
lpc
lpcnet
LPNMHDR
LPNMTTDISPINFO
lround
lte
luma
Luma
maj
manualdrop
maskcache
maxabs
maxcorr
MAXFACTORS
maxperiod
maxstep
memalign
memid
memneeded
MENUINFO
MFSTARTUP
mfxhw
mic
middledrop
minperiod
MIPSr
MJPEG
MMRESULT
momentumreversal
movc
mrate
mrt
MULBYSCALAR
MULC
MWERKS
mycfg
narrowstrip
nbak
nbytes
ncapture
nchw
ncm
nduplicates
nfft
NHWC
niterations
nmonitor
nnet
NONCLIENTMETRICS
NONOTIFY
nonvle
normf
nredraw
nstop
nsubpixel
ntorn
numthreads
nvw
Octasic
osc
OSCE
ovflw
OWNERDRAW
PBGRA
periodictrap
pfdc
pillarbox
pfdc
playhead
pnmh
pointerreuse
PPW
prereq
PSHR
pstdint
PSWA
pwfx
QCONST
qpc
Qpc
quantums
qweight
RCSEGMODEL
RCZOOMITSCR
readback
READERF
realcapture
REFKNOWNFOLDERID
relu
reposted
RETURNCMD
rnn
rnnoise
rotateleft
rsqrt
rtcd
RTEXT
RTH
rtvs
SCALEIN
SCALEOUT
SCREENSAVE
SCRNSAVE
SCRNSAVECONFIGURE
@@ -273,80 +138,43 @@ scrnsavw
Scrnsavw
scrollramp
SCROLLSIZEGRIP
selfie
selftest
SETBARCOLOR
SETBKCOLOR
SETDEFID
SETRECT
SETSCREENSAVETIMEOUT
SETTIPSIDE
sgem
sgemv
sgv
SHAREMODE
SHAREVIOLATION
shortlist
simde
siv
slowthenfast
smallstart
SNIPOCR
softmax
sqrtf
SROUND
srvs
ssi
startuprecovery
stdint
stf
stopafter
STREAMFLAGS
SUBFROM
subias
submix
sxx
sxy
symbian
synthesising
syy
tallportal
TBTS
tci
tcsicmp
TEXTCALLBACK
TEXTMETRIC
tgsm
THIRDPARTY
tinystep
tme
toolbars
TOOLINFO
TRACKMOUSEEVENT
TRIANGLELIST
TTM
TTN
TWID
UADD
uav
uavs
uge
Unadvise
upscaled
upscales
USUB
utof
vad
vaddq
vaddvq
valgrind
Valin
vandq
vblank
vcgeq
vdup
vectorizer
VERTID
VIDCAP
vld
vle
@@ -356,7 +184,6 @@ vminq
vmlal
vmull
vqaddq
VSHR
vshrn
vsntprintf
vsnwprintf
@@ -367,9 +194,7 @@ WAVEFORMATEXTENSIBLE
webcam
Webcam
webcams
Wextra
wfopen
WGC
wideportal
wil
WMU
@@ -377,46 +202,11 @@ wrapjump
wtol
WTSSESSION
WTSUn
wxyz
xchg
xcorr
XEnd
Xfl
Xiang
Xiph
xmmintrin
xptr
xshift
XStart
XStep
xxxy
xxyx
xxyz
xyw
xywx
xyxx
xyxz
xyzw
xyzx
xzwx
xzxx
Yfl
YInternal
yshift
YUV
yyyx
yyzw
yzw
yzwy
yzyy
Zhou
Zhu
ZMBS
zncc
Zncc
ZNCC
zrh
zwzz
zyzw
zzwz
zzzw

View File

@@ -113,6 +113,7 @@ azman
azureaiinference
azureinference
azureopenai
Backlight
backticks
Badflags
Badmode
@@ -415,7 +416,6 @@ DISPLAYFLAGS
DISPLAYFREQUENCY
displayname
DISPLAYORIENTATION
DISPLAYPORT
divyan
DLGFRAME
dlgmodalframe
@@ -471,6 +471,7 @@ DWMWINDOWATTRIBUTE
DWMWINDOWMAXIMIZEDCHANGE
DWORDLONG
dworigin
DWRITE
dxgi
Dxva
eab
@@ -861,7 +862,6 @@ jjw
jobject
JOBOBJECT
jpe
JPN
jpnime
jrsoftware
Jsons
@@ -994,8 +994,8 @@ LTM
LTRREADING
luid
lusrmgr
LVDS
LWA
LWIN
LZero
MAGTRANSFORM
makeappx
@@ -1058,8 +1058,6 @@ MINIMIZESTART
MINMAXINFO
minwindef
Mip
Miracast
miracast
mkdn
mlcfg
mmc
@@ -1205,6 +1203,7 @@ nonclient
NONCLIENTMETRICSW
NONELEVATED
nonspace
nonstd
NOOWNERZORDER
NOPARENTNOTIFY
NOPREFIX
@@ -1387,6 +1386,7 @@ popups
POPUPWINDOW
portfile
POSITIONITEM
Postbot
POWERBROADCAST
powerdisplay
POWERDISPLAYMODULEINTERFACE
@@ -1418,6 +1418,7 @@ Prefixer
Premul
prependpath
prepopulate
Prereq
prevhost
previewer
PREVIEWHANDLERFRAMEINFO
@@ -1816,7 +1817,6 @@ svchost
SVGIn
SVGIO
svgz
SVIDEO
SVSI
SWFO
swp
@@ -1995,6 +1995,7 @@ valuegenerator
VARTYPE
vbcscompiler
vcamp
VCENTER
vcgtq
VCINSTALLDIR
vcp
@@ -2032,6 +2033,7 @@ vorrq
VOS
vpaddlq
vqsubq
VREDRAW
vreinterpretq
VSC
VSCBD

View File

@@ -312,9 +312,3 @@ ms-windows-store://\S+
# ANSI color codes
(?:\\(?:u00|x)1[Bb]|\\03[1-7]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+)*m
# Special licenses text from RNNoise (BSD-style disclaimer: ``AS IS'')
``AS IS''
# Old school moniker for macOS from RNNoise
MacOS

3
.gitignore vendored
View File

@@ -19,9 +19,6 @@
[Rr]eleases/
x64/
x86/
!**/rnnoise/
!**/rnnoise/x86/
!**/rnnoise/x86/**
ARM64/
bld/
[Bb]in/

View File

@@ -1004,6 +1004,10 @@
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/ShortcutGuide/ShortcutGuide.UnitTests/ShortcutGuide.UnitTests.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/modules/ShortcutGuide/ShortcutGuideModuleInterface/ShortcutGuideModuleInterface.vcxproj" Id="e487304a-b1fb-4e6b-8e70-014051af5b99" />
</Folder>
<Folder Name="/modules/Workspaces/">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 258 KiB

View File

@@ -195,10 +195,18 @@ Special sections start with an identifier enclosed between `<` and `>`. This dec
A string array of all the keys that need to be pressed. If a number is supplied, it should be read as a [KeyCode](https://learn.microsoft.com/windows/win32/inputdev/virtual-key-codes) and displayed accordingly (based on the Keyboard Layout of the user).
**Literal digit keys**:
Because a bare number is interpreted as a virtual-key code, a literal digit key must be authored using the `<N>` notation (the digit enclosed between `<` and `>`), where `N` is `0``9`. For example, `<9>` represents the literal `9` key (as in the "switch to the last tab" shortcut), not the virtual-key code `9` (which is `Tab`). The interpreter strips the brackets and displays just the digit.
This applies only to a single literal digit. A range such as `1 - 8` is a free-form label, not a key, and is supplied verbatim (the brackets would only be trimmed from the ends, so `<1> - <8>` would not render as intended).
**Special keys**:
Special keys are enclosed between `<` and `>` and correspond to a key that should be displayed in a certain way. If the interpreter of the manifest file can't understand the content, the brackets should be left out.
By convention these tokens are written as double-quoted strings in the YAML (for example `"<Enter>"` and `"<9>"`), matching the quoting used for punctuation key values. YAML treats the quoted and unquoted forms identically, so quoting is for consistency rather than a strict requirement for bracketed tokens.
|Name|Description|
|----|-----------|
|`<Office>`| Corresponds to the Office key on some Windows keyboards |

View File

@@ -73,8 +73,7 @@
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<AdditionalOptions>%(AdditionalOptions) /bigobj</AdditionalOptions>
<!-- TODO: _SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS: suppress VS 2026 STL hard error for <experimental/coroutine> until the code is ported to <coroutine> -->
<PreprocessorDefinitions>_WINRT_DLL;WINRT_LEAN_AND_MEAN;_SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_WINRT_DLL;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>../../..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
</ClCompile>

View File

@@ -210,7 +210,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 0
- "<0>"
- SectionName: Formatting
Properties:
- Name: Bold

View File

@@ -1542,7 +1542,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 3
- "<3>"
- Name: Move earlier or later by number of frames specified for stroke Duration
Shortcut:
- Win: false

View File

@@ -642,7 +642,7 @@ Shortcuts:
Shift: true
Alt: true
Keys:
- 3
- "<3>"
- Name: Show document template
Shortcut:
- Win: false
@@ -810,7 +810,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 5
- "<5>"
- Name: Release guides
Shortcut:
- Win: false
@@ -818,7 +818,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 5
- "<5>"
- Name: Show/ hide smart guides
Shortcut:
- Win: false
@@ -925,7 +925,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 6
- "<6>"
- Name: Select the object above the current selection
Shortcut:
- Win: false
@@ -965,7 +965,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 2
- "<2>"
- Name: Unlock a selection
Shortcut:
- Win: false
@@ -973,7 +973,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 2
- "<2>"
- Name: Hide a selection
Shortcut:
- Win: false
@@ -981,7 +981,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 3
- "<3>"
- Name: Show all selections
Shortcut:
- Win: false
@@ -989,7 +989,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 3
- "<3>"
- Name: Move selection in user-defined increments
Shortcut:
- Win: false
@@ -1013,7 +1013,7 @@ Shortcuts:
Shift: true
Alt: true
Keys:
- 2
- "<2>"
- Name: Bring a selection forward
Shortcut:
- Win: false
@@ -1071,7 +1071,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 8
- "<8>"
- Name: Release a compound path
Shortcut:
- Win: false
@@ -1079,7 +1079,7 @@ Shortcuts:
Shift: true
Alt: true
Keys:
- 8
- "<8>"
- Name: Edit a pattern
Shortcut:
- Win: false
@@ -1261,7 +1261,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 4
- "<4>"
- Name: Move an object
Shortcut:
- Win: false
@@ -1285,7 +1285,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 7
- "<7>"
- Name: Release a clipping mask
Shortcut:
- Win: false
@@ -1293,7 +1293,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 7
- "<7>"
- Name: Toggle between fill and stroke
Shortcut:
- Win: false
@@ -1641,7 +1641,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 8
- "<8>"
- Name: Insert copyright symbol
Shortcut:
- Win: false
@@ -1665,7 +1665,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 7
- "<7>"
- Name: Insert section symbol
Shortcut:
- Win: false
@@ -1673,7 +1673,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 6
- "<6>"
- Name: Insert trademark symbol
Shortcut:
- Win: false
@@ -1681,7 +1681,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 2
- "<2>"
- Name: Insert registered trademark symbol
Shortcut:
- Win: false

View File

@@ -1036,7 +1036,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 5
- "<5>"
- Name: Redraw screen
Shortcut:
- Win: false
@@ -1060,7 +1060,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 2
- "<2>"
- Name: Switch to next/previous document window
Shortcut:
- Win: false
@@ -1155,7 +1155,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 6
- "<6>"
- Name: Toggle Character/Paragraph text attributes mode
Shortcut:
- Win: false
@@ -1163,7 +1163,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 7
- "<7>"
- Name: Display the pop-up menu that has focus
Shortcut:
- Win: false
@@ -1301,7 +1301,7 @@ Shortcuts:
Shift: true
Alt: true
Keys:
- 1
- "<1>"
- Name: Show Magenta plate
Shortcut:
- Win: false
@@ -1309,7 +1309,7 @@ Shortcuts:
Shift: true
Alt: true
Keys:
- 2
- "<2>"
- Name: Show Yellow plate
Shortcut:
- Win: false
@@ -1317,7 +1317,7 @@ Shortcuts:
Shift: true
Alt: true
Keys:
- 3
- "<3>"
- Name: Show Black plate
Shortcut:
- Win: false
@@ -1325,7 +1325,7 @@ Shortcuts:
Shift: true
Alt: true
Keys:
- 4
- "<4>"
- Name: Show 1st Spot plate
Shortcut:
- Win: false
@@ -1333,7 +1333,7 @@ Shortcuts:
Shift: true
Alt: true
Keys:
- 5
- "<5>"
- Name: Show 2nd Spot plate
Shortcut:
- Win: false
@@ -1341,7 +1341,7 @@ Shortcuts:
Shift: true
Alt: true
Keys:
- 6
- "<6>"
- Name: Show 3rd Spot plate
Shortcut:
- Win: false
@@ -1349,7 +1349,7 @@ Shortcuts:
Shift: true
Alt: true
Keys:
- 7
- "<7>"
- SectionName: Transform panel
Properties:
- Name: Apply value and copy object

View File

@@ -803,7 +803,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 1
- "<1>"
- Name: Switch to Hand tool (when not in text-edit mode)
Shortcut:
- Win: false
@@ -1309,7 +1309,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 1
- "<1>"
- Name: Tone Curve panel
Shortcut:
- Win: false
@@ -1317,7 +1317,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 2
- "<2>"
- Name: Detail panel
Shortcut:
- Win: false
@@ -1325,7 +1325,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 3
- "<3>"
- Name: HSL/Grayscale panel
Shortcut:
- Win: false
@@ -1333,7 +1333,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 4
- "<4>"
- Name: Split Toning panel
Shortcut:
- Win: false
@@ -1341,7 +1341,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 5
- "<5>"
- Name: Lens Corrections panel
Shortcut:
- Win: false
@@ -1349,7 +1349,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 6
- "<6>"
- Name: Camera Calibration panel
Shortcut:
- Win: false
@@ -1357,7 +1357,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 7
- "<7>"
- Name: Presets panel
Shortcut:
- Win: false
@@ -1365,7 +1365,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 9
- "<9>"
- Name: Open Snapshots panel
Shortcut:
- Win: false
@@ -1373,7 +1373,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 9
- "<9>"
- Name: Parametric Curve Targeted Adjustment tool
Shortcut:
- Win: false
@@ -1665,7 +1665,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 6
- "<6>"
- Name: (Filmstrip mode) Add yellow label
Shortcut:
- Win: false
@@ -1673,7 +1673,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 7
- "<7>"
- Name: (Filmstrip mode) Add green label
Shortcut:
- Win: false
@@ -1681,7 +1681,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 8
- "<8>"
- Name: (Filmstrip mode) Add blue label
Shortcut:
- Win: false
@@ -1689,7 +1689,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 9
- "<9>"
- Name: (Filmstrip mode) Add purple label
Shortcut:
- Win: false
@@ -1697,7 +1697,7 @@ Shortcuts:
Shift: true
Alt: false
Keys:
- 0
- "<0>"
- Name: Camera Raw preferences
Shortcut:
- Win: false
@@ -1936,7 +1936,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 0
- "<0>"
- Name: Cycle through blending modes
Shortcut:
- Win: false
@@ -2433,7 +2433,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 2
- "<2>"
- Name: Delete adjustment layer
Shortcut:
- Win: false

View File

@@ -407,7 +407,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 1
- "<1>"
- Name: Edge select mode
Shortcut:
- Win: false
@@ -415,7 +415,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 2
- "<2>"
- Name: Face select mode
Shortcut:
- Win: false
@@ -423,7 +423,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 3
- "<3>"
- Name: Extrude region
Shortcut:
- Win: false

View File

@@ -806,7 +806,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 1
- "<1>"
- Name: Set opacity to 50
Shortcut:
- Win: false
@@ -814,7 +814,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 5
- "<5>"
- Name: Set opacity to 100
Shortcut:
- Win: false
@@ -822,7 +822,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 0
- "<0>"
- SectionName: Arrange
Properties:
- Name: Bring forward

View File

@@ -489,7 +489,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 1
- "<1>"
- SectionName: Edit
Properties:
- Name: Undo

View File

@@ -63,7 +63,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 1
- "<1>"
- Name: Jump to rightmost tab
Shortcut:
- Win: false
@@ -71,7 +71,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 9
- "<9>"
- Name: Open home page in current tab
Shortcut:
- Win: false
@@ -424,7 +424,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 0
- "<0>"
- Name: Scroll down a screen
Shortcut:
- Win: false

View File

@@ -21,7 +21,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 1
- "<1>"
- Name: Show Intention Actions
Recommended: true
Shortcut:
@@ -778,7 +778,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 1
- "<1>"
- Name: Show Bookmarks window
Shortcut:
- Win: false
@@ -786,7 +786,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 2
- "<2>"
- Name: Show Find window
Shortcut:
- Win: false
@@ -794,7 +794,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 3
- "<3>"
- Name: Show Run window
Shortcut:
- Win: false
@@ -802,7 +802,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 4
- "<4>"
- Name: Show Debug window
Shortcut:
- Win: false
@@ -810,7 +810,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 5
- "<5>"
- Name: Show Problems window
Shortcut:
- Win: false
@@ -818,7 +818,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 6
- "<6>"
- Name: Show Structure window
Shortcut:
- Win: false
@@ -826,7 +826,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 7
- "<7>"
- Name: Show Services window
Shortcut:
- Win: false
@@ -834,7 +834,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 8
- "<8>"
- Name: Show Version Control window
Shortcut:
- Win: false
@@ -842,7 +842,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 9
- "<9>"
- Name: Show Commit window
Shortcut:
- Win: false
@@ -850,7 +850,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 0
- "<0>"
- Name: Show Terminal window
Shortcut:
- Win: false

View File

@@ -45,7 +45,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 1
- "<1>"
- Name: Switch to the last tab
Shortcut:
- Win: false
@@ -53,7 +53,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 9
- "<9>"
- Name: Close the current tab
Shortcut:
- Win: false
@@ -479,7 +479,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 0
- "<0>"
- Name: Stop loading page; close dialog or pop-up
Shortcut:
- Win: false

View File

@@ -492,7 +492,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 1
- "<1>"
- Name: Focus into Second Editor Group
Shortcut:
- Win: false
@@ -500,7 +500,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 2
- "<2>"
- Name: Focus into Third Editor Group
Shortcut:
- Win: false
@@ -508,7 +508,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 3
- "<3>"
- Name: Move Editor Left
Shortcut:
- Win: false

View File

@@ -202,7 +202,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 0
- "<0>"
- SectionName: Editing
Properties:
- Name: Copy
@@ -485,7 +485,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 1
- "<1>"
- Name: Go to last tab
Shortcut:
- Win: false
@@ -493,7 +493,7 @@ Shortcuts:
Shift: false
Alt: false
Keys:
- 9
- "<9>"
- Name: Move tab left
Shortcut:
- Win: false

View File

@@ -0,0 +1,463 @@
PackageName: Postman.Postman
Name: Postman
WindowFilter: "Postman.exe"
BackgroundProcess: false
Shortcuts:
- SectionName: Tabs
Properties:
- Name: Close tab
Recommended: true
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- W
- Name: Force close tab
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: true
Keys:
- W
- Name: Switch to next tab
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- Tab
- Name: Switch to previous tab
Shortcut:
- Win: false
Ctrl: true
Shift: true
Alt: false
Keys:
- Tab
- Name: Switch to tab at position (18)
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- '1 - 8'
- Name: Switch to last tab
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- "<9>"
- Name: Reopen last closed tab
Shortcut:
- Win: false
Ctrl: true
Shift: true
Alt: false
Keys:
- T
- Name: New runner tab
Shortcut:
- Win: false
Ctrl: true
Shift: true
Alt: false
Keys:
- R
- Name: Search tabs
Shortcut:
- Win: false
Ctrl: true
Shift: true
Alt: false
Keys:
- A
- SectionName: Sidebar
Properties:
- Name: Search sidebar
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- F
- Name: Next item
Shortcut:
- Win: false
Ctrl: false
Shift: false
Alt: false
Keys:
- "<Down>"
- Name: Previous item
Shortcut:
- Win: false
Ctrl: false
Shift: false
Alt: false
Keys:
- "<Up>"
- Name: Expand item
Shortcut:
- Win: false
Ctrl: false
Shift: false
Alt: false
Keys:
- "<Right>"
- Name: Expand all
Shortcut:
- Win: false
Ctrl: false
Shift: false
Alt: true
Keys:
- "<Right>"
- Name: Collapse item
Shortcut:
- Win: false
Ctrl: false
Shift: false
Alt: false
Keys:
- "<Left>"
- Name: Collapse all
Shortcut:
- Win: false
Ctrl: false
Shift: false
Alt: true
Keys:
- "<Left>"
- Name: Select item
Shortcut:
- Win: false
Ctrl: false
Shift: false
Alt: false
Keys:
- "<Enter>"
- Name: Rename item
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- E
- Name: Cut item
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- X
- Name: Copy item
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- C
- Name: Paste item
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- V
- Name: Duplicate item
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- D
- Name: Delete item
Shortcut:
- Win: false
Ctrl: false
Shift: false
Alt: false
Keys:
- "<Delete>"
- SectionName: Request
Properties:
- Name: Request URL
Recommended: true
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- L
- Name: Save request
Recommended: true
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- S
- Name: Save request as
Shortcut:
- Win: false
Ctrl: true
Shift: true
Alt: false
Keys:
- S
- Name: Send request
Recommended: true
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- "<Enter>"
- Name: Send and download request
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: true
Keys:
- "<Enter>"
- SectionName: Interface
Properties:
- Name: Zoom in
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- Plus
- Name: Zoom out
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- Minus
- Name: Reset zoom
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- "<0>"
- Name: Toggle two-pane view
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: true
Keys:
- V
- Name: Toggle left sidebar
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- "\\"
- Name: Toggle right sidebar
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: true
Keys:
- "\\"
- Name: Toggle workbench
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: true
Keys:
- M
- Name: Swap sidebars
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: true
Keys:
- S
- Name: Reset layout
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: true
Keys:
- R
- Name: Environment selector
Shortcut:
- Win: false
Ctrl: false
Shift: false
Alt: true
Keys:
- E
- SectionName: Window and modals
Properties:
- Name: New…
Recommended: true
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- N
- Name: New Postman window
Shortcut:
- Win: false
Ctrl: true
Shift: true
Alt: false
Keys:
- N
- Name: New console window
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: true
Keys:
- C
- Name: Find
Shortcut:
- Win: false
Ctrl: true
Shift: true
Alt: false
Keys:
- F
- Name: Import
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- O
- Name: Settings
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- ","
- Name: Open shortcut help
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- "/"
- Name: Search
Recommended: true
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- K
- Name: Search in current workspace
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: true
Keys:
- K
- Name: Open Postbot
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: true
Keys:
- P
- Name: Open Vault
Shortcut:
- Win: false
Ctrl: true
Shift: true
Alt: false
Keys:
- V
- Name: Open browser tab
Shortcut:
- Win: false
Ctrl: true
Shift: true
Alt: false
Keys:
- B
- Name: Cancel conversation
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- C
- Name: Accept all
Shortcut:
- Win: false
Ctrl: true
Shift: true
Alt: false
Keys:
- Y
- Name: Reject all
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- "<Escape>"
- SectionName: Console
Properties:
- Name: Clear console
Shortcut:
- Win: false
Ctrl: true
Shift: true
Alt: false
Keys:
- K
- Name: Show/hide console
Shortcut:
- Win: false
Ctrl: true
Shift: false
Alt: false
Keys:
- "`"

View File

@@ -241,7 +241,7 @@ Shortcuts:
Shift: true
Alt: false
Keys:
- 1
- "<1>"
- Name: Browse DMs
Shortcut:
- Win: false
@@ -249,7 +249,7 @@ Shortcuts:
Shift: true
Alt: false
Keys:
- 2
- "<2>"
- Name: Open the Activity view
Shortcut:
- Win: false
@@ -265,7 +265,7 @@ Shortcuts:
Shift: true
Alt: false
Keys:
- 0
- "<0>"
- Name: Open the Threads view
Shortcut:
- Win: false
@@ -525,7 +525,7 @@ Shortcuts:
Shift: true
Alt: false
Keys:
- 9
- "<9>"
- Name: Inline code selected text
Shortcut:
- Win: false
@@ -549,7 +549,7 @@ Shortcuts:
Shift: true
Alt: false
Keys:
- 8
- "<8>"
- Name: Numbered list
Shortcut:
- Win: false
@@ -557,7 +557,7 @@ Shortcuts:
Shift: true
Alt: false
Keys:
- 7
- "<7>"
- Name: Apply markdown formatting
Shortcut:
- Win: false
@@ -583,7 +583,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 0
- "<0>"
- Name: Big heading
Shortcut:
- Win: false
@@ -591,7 +591,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 1
- "<1>"
- Name: Medium heading
Shortcut:
- Win: false
@@ -599,7 +599,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 2
- "<2>"
- Name: Small heading
Shortcut:
- Win: false
@@ -607,7 +607,7 @@ Shortcuts:
Shift: false
Alt: true
Keys:
- 3
- "<3>"
- Name: Checklist
Shortcut:
- Win: false
@@ -615,7 +615,7 @@ Shortcuts:
Shift: true
Alt: false
Keys:
- 0
- "<0>"
- Name: Bulleted list
Shortcut:
- Win: false
@@ -623,7 +623,7 @@ Shortcuts:
Shift: true
Alt: false
Keys:
- 8
- "<8>"
- Name: Numbered list
Shortcut:
- Win: false
@@ -631,7 +631,7 @@ Shortcuts:
Shift: true
Alt: false
Keys:
- 7
- "<7>"
- Name: Toggle heading and list styles
Shortcut:
- Win: false

View File

@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using ShortcutGuide.Models;
@@ -32,16 +33,27 @@ namespace ShortcutGuide.Helpers
list.Add(shortcutEntry);
}
// Persist on a best-effort basis. The in-memory pinned list is the source of truth
// for the rest of the session; failing to write should not crash the overlay
// (Pin/Unpin runs from a synchronous UI handler).
Save();
PinnedShortcutsChanged?.Invoke(null, appName);
}
public static void Save()
{
string serialized = JsonSerializer.Serialize(App.PinnedShortcuts);
string pinnedPath = SettingsUtils.Default.GetSettingsFilePath(ShortcutGuideSettings.ModuleName, "Pinned.json");
File.WriteAllText(pinnedPath, serialized);
try
{
string serialized = JsonSerializer.Serialize(App.PinnedShortcuts);
string pinnedPath = SettingsUtils.Default.GetSettingsFilePath(ShortcutGuideSettings.ModuleName, "Pinned.json");
File.WriteAllText(pinnedPath, serialized);
}
catch (Exception ex) when (ex is IOException
or UnauthorizedAccessException
or JsonException)
{
Logger.LogError("Failed to persist Shortcut Guide pinned shortcuts; keeping in-memory state.", ex);
}
}
}
}

View File

@@ -2,9 +2,12 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Telemetry;
using Microsoft.UI.Xaml;
@@ -31,21 +34,39 @@ namespace ShortcutGuide
public App()
{
this.InitializeComponent();
// Register process-wide exception handlers so a stray exception (e.g. an IO failure
// during a fire-and-forget UI handler, or a background Task fault) gets logged
// instead of taking the overlay down with an unhandled access violation in coreclr.
// Without these the runtime tears the process down before our local catches can run.
this.UnhandledException += App_UnhandledException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
}
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
this.LoadData();
MainWindow = new MainWindow();
TaskBarWindow = new TaskbarWindow();
MainWindow.Activate();
MainWindow.Closed += (_, _) =>
try
{
PowerToysTelemetry.Log.WriteEvent(new ShortcutGuideSessionEvent(
MainWindow.SessionDurationMs,
MainWindow.CloseType));
TaskBarWindow.Close();
};
this.LoadData();
MainWindow = new MainWindow();
TaskBarWindow = new TaskbarWindow();
MainWindow.Activate();
MainWindow.Closed += (_, _) =>
{
PowerToysTelemetry.Log.WriteEvent(new ShortcutGuideSessionEvent(
MainWindow.SessionDurationMs,
MainWindow.CloseType));
TaskBarWindow.Close();
};
}
catch (Exception ex)
{
// Any failure in launch is fatal for this short-lived overlay; log and exit
// cleanly rather than letting WinUI surface a generic crash dialog.
Logger.LogError("Failed to launch Shortcut Guide.", ex);
Environment.Exit(1);
}
}
private void LoadData()
@@ -63,18 +84,53 @@ namespace ShortcutGuide
PinnedShortcuts = loaded;
}
}
catch (JsonException)
catch (Exception ex) when (ex is JsonException
or IOException
or UnauthorizedAccessException)
{
// Fall back to the empty default if the file is corrupt.
// Fall back to the empty default if the file is corrupt or unreadable.
Logger.LogWarning($"Failed to load pinned shortcuts from '{pinnedPath}'. Falling back to empty list. Reason: {ex.Message}");
}
}
ShortcutGuideSettings = SettingsRepository<ShortcutGuideSettings>.GetInstance(settingsUtils).SettingsConfig;
ShortcutGuideProperties = ShortcutGuideSettings.Properties;
try
{
#pragma warning disable CA1869 // Cache and reuse 'JsonSerializerOptions' instances
settingsUtils.SaveSettings(JsonSerializer.Serialize(App.ShortcutGuideSettings, new JsonSerializerOptions { WriteIndented = true }), "Shortcut Guide");
settingsUtils.SaveSettings(JsonSerializer.Serialize(App.ShortcutGuideSettings, new JsonSerializerOptions { WriteIndented = true }), "Shortcut Guide");
#pragma warning restore CA1869 // Cache and reuse 'JsonSerializerOptions' instances
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
{
// Persisting the round-tripped settings is best-effort; the in-memory copy is still valid.
Logger.LogWarning($"Failed to persist Shortcut Guide settings on launch. Reason: {ex.Message}");
}
}
private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
{
// Exceptions raised on the UI thread land here. Mark handled so the runtime
// does not terminate the process; the overlay can usually continue.
Logger.LogError("Unhandled UI exception in Shortcut Guide.", e.Exception);
e.Handled = true;
}
private static void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e)
{
// Background-thread exceptions reach here as a last resort; we cannot prevent
// termination when IsTerminating is true, but at least we leave a log trail.
if (e.ExceptionObject is Exception ex)
{
Logger.LogError($"Unhandled background exception in Shortcut Guide (IsTerminating={e.IsTerminating}).", ex);
}
}
private static void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e)
{
Logger.LogError("Unobserved Task exception in Shortcut Guide.", e.Exception);
e.SetObserved();
}
}
}

View File

@@ -237,37 +237,54 @@ namespace ShortcutGuide
private void SetWindowPosition()
{
if (!this._hasMovedToRightMonitor)
try
{
NativeMethods.GetCursorPos(out NativeMethods.POINT lpPoint);
AppWindow.Move(new NativeMethods.POINT { Y = lpPoint.Y - ((int)Height / 2), X = lpPoint.X - ((int)Width / 2) });
this._hasMovedToRightMonitor = true;
if (!this._hasMovedToRightMonitor)
{
NativeMethods.GetCursorPos(out NativeMethods.POINT lpPoint);
AppWindow.Move(new NativeMethods.POINT { Y = lpPoint.Y - ((int)Height / 2), X = lpPoint.X - ((int)Width / 2) });
this._hasMovedToRightMonitor = true;
}
var hwnd = WindowNative.GetWindowHandle(this);
float dpi = DpiHelper.GetDPIScaleForWindow(hwnd);
Rect monitorRect = DisplayHelper.GetWorkAreaForDisplayWithWindow(hwnd);
var windowPosition = (ShortcutGuideWindowPosition)App.ShortcutGuideProperties.WindowPosition.Value;
// App.TaskBarWindow / its AppWindow can briefly be null during the reentrant
// Hide → Activate → BringToFront chain triggered from SelectionChanged. When the
// taskbar window is not currently observable, skip the overlap adjustment instead
// of crashing the overlay (issue #48448).
var taskbarWindow = App.TaskBarWindow?.AppWindow;
bool taskbarOnLeft = false;
bool taskbarOnRight = false;
if (taskbarWindow is not null)
{
taskbarOnLeft = taskbarWindow.IsVisible && taskbarWindow.Position.X < AppWindow.Position.X + Width && windowPosition == ShortcutGuideWindowPosition.Left;
taskbarOnRight = taskbarWindow.IsVisible && taskbarWindow.Position.X + taskbarWindow.Size.Width > AppWindow.Position.X && windowPosition == ShortcutGuideWindowPosition.Right;
}
double newHeight = monitorRect.Height / dpi;
if (taskbarWindow is not null && (taskbarOnLeft || taskbarOnRight))
{
newHeight -= taskbarWindow.Size.Height;
}
MaxHeight = newHeight;
MinHeight = newHeight;
Height = newHeight;
int xPosition = windowPosition == ShortcutGuideWindowPosition.Right
? (int)(monitorRect.X + monitorRect.Width) - (int)(Width * dpi)
: (int)monitorRect.X;
this.MoveAndResize(xPosition, (int)monitorRect.Y, Width, Height);
}
var hwnd = WindowNative.GetWindowHandle(this);
float dpi = DpiHelper.GetDPIScaleForWindow(hwnd);
Rect monitorRect = DisplayHelper.GetWorkAreaForDisplayWithWindow(hwnd);
var windowPosition = (ShortcutGuideWindowPosition)App.ShortcutGuideProperties.WindowPosition.Value;
var taskbarWindow = App.TaskBarWindow.AppWindow;
bool taskbarOnLeft = taskbarWindow.IsVisible && taskbarWindow.Position.X < AppWindow.Position.X + Width && windowPosition == ShortcutGuideWindowPosition.Left;
bool taskbarOnRight = taskbarWindow.IsVisible && taskbarWindow.Position.X + taskbarWindow.Size.Width > AppWindow.Position.X && windowPosition == ShortcutGuideWindowPosition.Right;
double newHeight = monitorRect.Height / dpi;
if (taskbarOnLeft || taskbarOnRight)
catch (Exception ex)
{
newHeight -= taskbarWindow.Size.Height;
Logger.LogError("Failed to set Shortcut Guide window position; keeping previous layout.", ex);
}
MaxHeight = newHeight;
MinHeight = newHeight;
Height = newHeight;
int xPosition = windowPosition == ShortcutGuideWindowPosition.Right
? (int)(monitorRect.X + monitorRect.Width) - (int)(Width * dpi)
: (int)monitorRect.X;
this.MoveAndResize(xPosition, (int)monitorRect.Y, Width, Height);
}
/// <summary>
@@ -282,25 +299,35 @@ namespace ShortcutGuide
return;
}
this._selectedAppName = selectedItem.Name;
App.CurrentAppName = this._selectedAppName;
this._shortcutFile = ManifestInterpreter.GetShortcutsOfApplication(this._selectedAppName);
App.TaskBarWindow.Hide();
if (this._shortcutFile is ShortcutFile file)
try
{
// Show the taskbar button window only when the selected app exposes the <TASKBAR1-9> section.
if (file.Shortcuts is not null && file.Shortcuts.Any(c => c.SectionName?.StartsWith("<TASKBAR1-9>", StringComparison.Ordinal) == true))
{
this._taskBarWindowActivated = true;
App.TaskBarWindow.Activate();
}
this._selectedAppName = selectedItem.Name;
App.CurrentAppName = this._selectedAppName;
this._shortcutFile = ManifestInterpreter.GetShortcutsOfApplication(this._selectedAppName);
// Reposition before navigating so the taskbar window does not clip into the main window.
this.SetWindowPosition();
this.ContentFrame.Navigate(
typeof(ShortcutsPage),
new ShortcutPageNavParam { ShortcutFile = file, AppName = this._selectedAppName });
App.TaskBarWindow?.Hide();
if (this._shortcutFile is ShortcutFile file)
{
// Show the taskbar button window only when the selected app exposes the <TASKBAR1-9> section.
if (file.Shortcuts is not null && file.Shortcuts.Any(c => c.SectionName?.StartsWith("<TASKBAR1-9>", StringComparison.Ordinal) == true))
{
this._taskBarWindowActivated = true;
App.TaskBarWindow?.Activate();
}
// Reposition before navigating so the taskbar window does not clip into the main window.
this.SetWindowPosition();
this.ContentFrame.Navigate(
typeof(ShortcutsPage),
new ShortcutPageNavParam { ShortcutFile = file, AppName = this._selectedAppName });
}
}
catch (Exception ex)
{
// Guard against exceptions during section navigation so the overlay does not close on the user.
// InitializeNavItemsAsync's catch interprets any exception bubbling out of the initial
// SelectedItem assignment as a fatal init failure and closes the window (issue #48448).
Logger.LogError($"Failed to handle Shortcut Guide section selection '{selectedItem.Name}'.", ex);
}
}

View File

@@ -30,54 +30,73 @@ namespace ShortcutGuide.ShortcutGuideXAML
public void UpdateTasklistButtons()
{
// This move ensures the window spawns on the same monitor as the main window
AppWindow.MoveInZOrderAtBottom();
AppWindow.Move(App.MainWindow.AppWindow.Position);
TasklistButton[] buttons = [];
// Wrap the entire body: this method runs from the ctor and from `Activated`,
// both of which can fire while MainWindow is closing or AppWindow is in a
// transient null state. An exception here used to crash the overlay because
// there was no caller-side try/catch (issue #48441).
try
{
buttons = TasklistPositions.GetButtons();
// This move ensures the window spawns on the same monitor as the main window.
// App.MainWindow / its AppWindow can briefly be null during the reentrant
// Hide → Activate → BringToFront chain triggered from SelectionChanged.
var mainAppWindow = App.MainWindow?.AppWindow;
if (mainAppWindow is null)
{
return;
}
AppWindow.MoveInZOrderAtBottom();
AppWindow.Move(mainAppWindow.Position);
TasklistButton[] buttons = [];
try
{
buttons = TasklistPositions.GetButtons();
}
catch (Exception ex)
{
Logger.LogError("Failed to enumerate taskbar buttons via TasklistPositions.GetButtons.", ex);
}
if (buttons.Length == 0)
{
AppWindow.Hide();
return;
}
float dpi = this.DPI;
double windowsLogoColumnWidth = this.WindowsLogoColumnWidth.Width.Value;
double windowHeight = 58;
double windowMargin = 8 * dpi;
double windowWidth = windowsLogoColumnWidth;
double xPosition = buttons[0].X - (windowsLogoColumnWidth * dpi);
double yPosition = this.WorkArea.Bottom - (windowHeight * dpi);
this.KeyHolder.Children.Clear();
foreach (TasklistButton b in buttons)
{
TaskbarIndicator indicator = new()
{
Label = b.Keynum >= 10 ? "0" : b.Keynum.ToString(CultureInfo.InvariantCulture),
Height = b.Height / dpi,
Width = b.Width / dpi,
};
windowWidth += indicator.Width;
this.KeyHolder.Children.Add(indicator);
double indicatorPos = (b.X - xPosition) / dpi;
Canvas.SetLeft(indicator, indicatorPos - windowsLogoColumnWidth);
}
this.MoveAndResize(xPosition - windowMargin, yPosition, windowWidth + (2 * windowMargin), windowHeight);
AppWindow.MoveInZOrderAtTop();
}
catch (Exception ex)
{
Logger.LogError("Failed to enumerate taskbar buttons via TasklistPositions.GetButtons.", ex);
Logger.LogError("Failed to update Shortcut Guide taskbar indicator window.", ex);
}
if (buttons.Length == 0)
{
AppWindow.Hide();
return;
}
float dpi = this.DPI;
double windowsLogoColumnWidth = this.WindowsLogoColumnWidth.Width.Value;
double windowHeight = 58;
double windowMargin = 8 * dpi;
double windowWidth = windowsLogoColumnWidth;
double xPosition = buttons[0].X - (windowsLogoColumnWidth * dpi);
double yPosition = this.WorkArea.Bottom - (windowHeight * dpi);
this.KeyHolder.Children.Clear();
foreach (TasklistButton b in buttons)
{
TaskbarIndicator indicator = new()
{
Label = b.Keynum >= 10 ? "0" : b.Keynum.ToString(CultureInfo.InvariantCulture),
Height = b.Height / dpi,
Width = b.Width / dpi,
};
windowWidth += indicator.Width;
this.KeyHolder.Children.Add(indicator);
double indicatorPos = (b.X - xPosition) / dpi;
Canvas.SetLeft(indicator, indicatorPos - windowsLogoColumnWidth);
}
this.MoveAndResize(xPosition - windowMargin, yPosition, windowWidth + (2 * windowMargin), windowHeight);
AppWindow.MoveInZOrderAtTop();
}
}
}

View File

@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ShortcutGuide.Converters;
using ShortcutGuide.Models;
namespace ShortcutGuide.UnitTests.ConvertersTests;
[TestClass]
public sealed class ShortcutDescriptionToKeysConverterTests
{
private static List<object> Convert(ShortcutDescription description)
=> new ShortcutDescriptionToKeysConverter().GetKeysList(description);
[TestMethod]
[DataRow("<0>")]
[DataRow("<1>")]
[DataRow("<8>")]
[DataRow("<9>")]
public void GetKeysList_LiteralDigitKey_IsPassedThroughVerbatim(string key)
{
// A literal digit key (e.g. Ctrl+9 "switch to last tab") is authored with the
// <N> convention so it is not parsed as a virtual-key code (VK 9 is Tab, VK 1 is
// the left mouse button, VK 0 is undefined). The converter forwards the token
// unchanged; KeyVisual strips the angle brackets when rendering.
var result = Convert(new ShortcutDescription(ctrl: true, shift: false, alt: false, win: false, keys: [key]));
CollectionAssert.AreEqual(new object[] { "Ctrl", key }, result);
}
[TestMethod]
public void GetKeysList_Modifiers_AreEmittedBeforeKeysInWinCtrlAltShiftOrder()
{
// Win -> 92, Ctrl -> "Ctrl", Alt -> "Alt", Shift -> 16, then the keys.
var result = Convert(new ShortcutDescription(ctrl: true, shift: true, alt: true, win: true, keys: ["A"]));
CollectionAssert.AreEqual(new object[] { 92, "Ctrl", "Alt", 16, "A" }, result);
}
[TestMethod]
public void GetKeysList_NonNumericKey_IsPassedThroughVerbatim()
{
// Non-numeric key strings (e.g. the "1 - 8" tab-range) render as-is.
var result = Convert(new ShortcutDescription(ctrl: true, shift: false, alt: false, win: false, keys: ["1 - 8"]));
CollectionAssert.AreEqual(new object[] { "Ctrl", "1 - 8" }, result);
}
[TestMethod]
public void GetKeysList_ArrowNameKey_MapsToVirtualKeyCode()
{
// Named arrow keys map to their VK codes (Up -> 38), independent of the digit handling.
var result = Convert(new ShortcutDescription(ctrl: false, shift: false, alt: false, win: false, keys: ["Up"]));
CollectionAssert.AreEqual(new object[] { 38 }, result);
}
}

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="$(RepoRoot)src\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<SelfContained>true</SelfContained>
<RuntimeIdentifier Condition="'$(Platform)' == 'x64'">win-x64</RuntimeIdentifier>
<RuntimeIdentifier Condition="'$(Platform)' == 'ARM64'">win-arm64</RuntimeIdentifier>
<IsPackable>false</IsPackable>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\tests\ShortcutGuide.UnitTests\</OutputPath>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSTest" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ShortcutGuide.Ui\ShortcutGuide.Ui.csproj" />
</ItemGroup>
</Project>

View File

@@ -34,17 +34,15 @@ namespace winrt
using namespace Windows::Devices::Enumeration;
}
AudioSampleGenerator::AudioSampleGenerator(bool captureMicrophone, bool captureSystemAudio, bool mixMicrophoneMono, bool useNoiseCancellation)
AudioSampleGenerator::AudioSampleGenerator(bool captureMicrophone, bool captureSystemAudio, bool micMonoMix)
: m_captureMicrophone(captureMicrophone)
, m_captureSystemAudio(captureSystemAudio)
, m_mixMicrophoneMono(mixMicrophoneMono)
, m_useNoiseCancellation(useNoiseCancellation)
, m_micMonoMix(micMonoMix)
{
OutputDebugStringA(("AudioSampleGenerator created, captureMicrophone=" +
std::string(captureMicrophone ? "true" : "false") +
", captureSystemAudio=" + std::string(captureSystemAudio ? "true" : "false") +
", mixMicrophoneMono=" + std::string(mixMicrophoneMono ? "true" : "false") +
", useNoiseCancellation=" + std::string(useNoiseCancellation ? "true" : "false") + "\n").c_str());
", micMonoMix=" + std::string(micMonoMix ? "true" : "false") + "\n").c_str());
m_audioEvent.create(wil::EventOptions::ManualReset);
m_endEvent.create(wil::EventOptions::ManualReset);
m_startEvent.create(wil::EventOptions::ManualReset);
@@ -160,24 +158,8 @@ winrt::IAsyncAction AudioSampleGenerator::InitializeAsync()
throw winrt::hresult_error(E_FAIL, L"Failed to initialize loopback audio capture!");
}
// Initialize noise suppressor for microphone audio if enabled
if (m_useNoiseCancellation && m_captureMicrophone)
{
m_noiseSuppressor = std::make_unique<NoiseSuppressor>();
OutputDebugStringA("Noise cancellation enabled for microphone\n");
}
m_audioGraph.QuantumStarted({ this, &AudioSampleGenerator::OnAudioQuantumStarted });
// Start the AudioGraph now so the microphone device begins warming up
// during the remaining recording initialization (transcoder setup, etc.).
// OnAudioQuantumStarted returns early while m_started is false, so audio
// samples are discarded until Start() is called. The side-effect of
// starting the graph early is that the system mic-active icon appears
// sooner, which also triggers a desktop-content change that helps
// unblock the WGC frame pool wait in OnMediaStreamSourceStarting.
m_audioGraph.Start();
m_asyncInitialized.SetEvent();
}
}
@@ -223,65 +205,67 @@ std::optional<winrt::MediaStreamSample> AudioSampleGenerator::TryGetNextSample()
}
}
// Wait for audio samples to become available, retrying on spurious wakes
// (e.g. when OnAudioQuantumStarted signals m_audioEvent but the quantum
// produced an empty buffer so m_samples is still empty).
for (;;)
{
auto lock = m_lock.lock_exclusive();
if (m_samples.empty() && m_endEvent.is_signaled())
{
auto lock = m_lock.lock_exclusive();
if (m_samples.empty() && m_endEvent.is_signaled())
{
return std::nullopt;
}
else if (!m_samples.empty())
{
std::optional result(m_samples.front());
m_samples.pop_front();
return result;
}
}
m_audioEvent.ResetEvent();
std::vector<HANDLE> events = { m_endEvent.get(), m_audioEvent.get() };
auto waitResult = WaitForMultipleObjectsEx(static_cast<DWORD>(events.size()), events.data(), false, INFINITE, false);
auto eventIndex = -1;
switch (waitResult)
{
case WAIT_OBJECT_0:
case WAIT_OBJECT_0 + 1:
eventIndex = waitResult - WAIT_OBJECT_0;
break;
}
WINRT_VERIFY(eventIndex >= 0);
auto signaledEvent = events[eventIndex];
if (signaledEvent == m_endEvent.get())
{
// End was signaled, but check for any remaining samples before returning nullopt
auto lock = m_lock.lock_exclusive();
if (!m_samples.empty())
{
std::optional result(m_samples.front());
m_samples.pop_front();
return result;
}
return std::nullopt;
}
// m_audioEvent was signaled — loop back to check m_samples again.
// If the quantum produced an empty buffer, m_samples will still be
// empty and we'll wait for the next quantum.
else if (!m_samples.empty())
{
std::optional result(m_samples.front());
m_samples.pop_front();
return result;
}
}
m_audioEvent.ResetEvent();
std::vector<HANDLE> events = { m_endEvent.get(), m_audioEvent.get() };
auto waitResult = WaitForMultipleObjectsEx(static_cast<DWORD>(events.size()), events.data(), false, INFINITE, false);
auto eventIndex = -1;
switch (waitResult)
{
case WAIT_OBJECT_0:
case WAIT_OBJECT_0 + 1:
eventIndex = waitResult - WAIT_OBJECT_0;
break;
}
WINRT_VERIFY(eventIndex >= 0);
auto signaledEvent = events[eventIndex];
if (signaledEvent == m_endEvent.get())
{
// End was signaled, but check for any remaining samples before returning nullopt
auto lock = m_lock.lock_exclusive();
if (!m_samples.empty())
{
std::optional result(m_samples.front());
m_samples.pop_front();
return result;
}
return std::nullopt;
}
else
{
auto lock = m_lock.lock_exclusive();
if (m_samples.empty())
{
// Spurious wake or race - no samples available
// If end is signaled, return nullopt
return m_endEvent.is_signaled() ? std::nullopt : std::optional<winrt::MediaStreamSample>{};
}
std::optional result(m_samples.front());
m_samples.pop_front();
return result;
}
}
void AudioSampleGenerator::Start(int64_t videoStartTimestamp)
void AudioSampleGenerator::Start()
{
CheckInitialized();
m_videoStartTimestamp = videoStartTimestamp;
auto expected = false;
if (m_started.compare_exchange_strong(expected, true))
{
OutputDebugStringW( L"[AudioGen] Start(): m_started set to true, setting m_startEvent\n" );
m_endEvent.ResetEvent();
m_startEvent.SetEvent();
@@ -300,7 +284,7 @@ void AudioSampleGenerator::Start(int64_t videoStartTimestamp)
m_loopbackCapture->Start();
}
// AudioGraph was already started in InitializeAsync for mic warmup.
m_audioGraph.Start();
}
}
@@ -627,21 +611,12 @@ void AudioSampleGenerator::CombineQueuedSamples()
void AudioSampleGenerator::OnAudioQuantumStarted(winrt::AudioGraph const& sender, winrt::IInspectable const& args)
{
// Don't process if we're not actively recording, but DO drain the
// output node so stale audio doesn't accumulate during mic warmup.
// Without this, the first GetFrame() after m_started becomes true
// would return several seconds of buffered audio, confusing the
// transcoder's A/V interleaving.
// Don't process if we're not actively recording
if (!m_started.load())
{
auto frame = m_audioOutputNode.GetFrame();
(void)frame; // discard
return;
}
static int s_quantumCount = 0;
s_quantumCount++;
{
auto lock = m_lock.lock_exclusive();
@@ -653,14 +628,6 @@ void AudioSampleGenerator::OnAudioQuantumStarted(winrt::AudioGraph const& sender
auto sampleBuffer = winrt::Buffer::CreateCopyFromMemoryBuffer(audioBuffer);
sampleBuffer.Length(audioBuffer.Length());
if( s_quantumCount <= 5 )
{
wchar_t dbg[256];
swprintf_s( dbg, L"[AudioGen] quantum #%d: audioBuffer.Length=%u sampleBuffer.Length=%u started=%d\n",
s_quantumCount, audioBuffer.Length(), sampleBuffer.Length(), m_started.load() ? 1 : 0 );
OutputDebugStringW( dbg );
}
// Calculate expected samples per quantum (~10ms at graph sample rate)
// AudioGraph uses 10ms quantums by default
uint32_t expectedSamplesPerQuantum = (m_graphSampleRate / 100) * m_graphChannels;
@@ -669,7 +636,7 @@ void AudioSampleGenerator::OnAudioQuantumStarted(winrt::AudioGraph const& sender
// Apply mono mixing to microphone audio if enabled
// This converts stereo mic input (with same signal on both channels) to true mono
// by averaging the channels and writing the result to both channels
if (m_mixMicrophoneMono && m_captureMicrophone && numMicSamples > 0 && m_graphChannels >= 2)
if (m_micMonoMix && m_captureMicrophone && numMicSamples > 0 && m_graphChannels >= 2)
{
float* micData = reinterpret_cast<float*>(sampleBuffer.data());
uint32_t numFrames = numMicSamples / m_graphChannels;
@@ -689,12 +656,6 @@ void AudioSampleGenerator::OnAudioQuantumStarted(winrt::AudioGraph const& sender
}
}
}
// Apply noise suppression to microphone audio before mixing with loopback
if (m_noiseSuppressor && m_captureMicrophone && numMicSamples > 0)
{
float* micData = reinterpret_cast<float*>(sampleBuffer.data());
m_noiseSuppressor->Process(micData, numMicSamples, m_graphChannels);
}
// Drain loopback samples regardless of whether we have mic audio
if (m_loopbackCapture)
@@ -772,25 +733,13 @@ void AudioSampleGenerator::OnAudioQuantumStarted(winrt::AudioGraph const& sender
if (sampleBuffer.Length() > 0)
{
// Rebase audio timestamps to the video's SystemRelativeTime domain.
// AudioGraph RelativeTime starts near 0 (or a few hundred ms after
// warmup draining), while video uses absolute SRT (~hours since boot).
// Without rebasing, the transcoder sees audio far behind video and
// starves video while trying to fill the gap with audio.
if (!m_hasTimestampOffset && timestamp.has_value())
{
m_timestampOffset = m_videoStartTimestamp - timestamp.value().count();
m_hasTimestampOffset = true;
}
auto adjustedTs = winrt::TimeSpan{ timestamp.value().count() + m_timestampOffset };
auto sample = winrt::MediaStreamSample::CreateFromBuffer(sampleBuffer, adjustedTs);
auto sample = winrt::MediaStreamSample::CreateFromBuffer(sampleBuffer, timestamp.value());
m_samples.push_back(sample);
const uint32_t sampleCount = sampleBuffer.Length() / sizeof(float);
const uint32_t frames = (m_graphChannels > 0) ? (sampleCount / m_graphChannels) : 0;
const int64_t durationTicks = (m_graphSampleRate > 0) ? (static_cast<int64_t>(frames) * 10000000LL / m_graphSampleRate) : 0;
m_lastSampleTimestamp = adjustedTs;
m_lastSampleTimestamp = timestamp.value();
m_lastSampleDuration = winrt::TimeSpan{ durationTicks };
m_hasLastSampleTimestamp = true;
}

View File

@@ -1,23 +1,18 @@
#pragma once
#include "LoopbackCapture.h"
#include "NoiseSuppressor.h"
#include <deque>
#include <optional>
#include <memory>
class AudioSampleGenerator
{
public:
AudioSampleGenerator(bool captureMicrophone = true, bool captureSystemAudio = true, bool mixMicrophoneMono = false, bool useNoiseCancellation = false);
AudioSampleGenerator(bool captureMicrophone = true, bool captureSystemAudio = true, bool micMonoMix = false);
~AudioSampleGenerator();
winrt::Windows::Foundation::IAsyncAction InitializeAsync();
winrt::Windows::Media::MediaProperties::AudioEncodingProperties GetEncodingProperties();
std::optional<winrt::Windows::Media::Core::MediaStreamSample> TryGetNextSample();
void Start(int64_t videoStartTimestamp = 0);
void Start();
void Stop();
private:
@@ -75,14 +70,5 @@ private:
std::atomic<bool> m_started = false;
bool m_captureMicrophone = true;
bool m_captureSystemAudio = true;
bool m_mixMicrophoneMono = false;
bool m_useNoiseCancellation = false;
std::unique_ptr<NoiseSuppressor> m_noiseSuppressor;
// Timestamp rebasing: audio RelativeTime → video SystemRelativeTime domain.
// Without this, the transcoder sees audio timestamps (~350ms) far below video
// timestamps (~111 billion ticks) and starves video while trying to fill the gap.
int64_t m_videoStartTimestamp = 0;
int64_t m_timestampOffset = 0;
bool m_hasTimestampOffset = false;
};
bool m_micMonoMix = false;
};

View File

@@ -1,859 +0,0 @@
//==============================================================================
//
// BackgroundBlur.cpp
//
// Windows ML-based person segmentation and background blur for the
// webcam overlay. Uses the built-in Windows.AI.MachineLearning API
// to load an ONNX segmentation model (e.g. MediaPipe SelfieSegmentation)
// and produce a per-pixel person mask, then blurs or replaces the
// background using an iterated box blur or a user-chosen image.
//
// Copyright (C) Mark Russinovich
// Sysinternals - www.sysinternals.com
//
//==============================================================================
#include "pch.h"
#include "BackgroundBlur.h"
#include <algorithm>
#include <cstring>
#include <wincodec.h>
#include <wil/com.h>
namespace winml = winrt::Windows::AI::MachineLearning;
namespace wf = winrt::Windows::Foundation::Collections;
// Defined in Zoomit.cpp; compiles to nothing in Release builds.
void OutputDebug(const TCHAR* format, ...);
//----------------------------------------------------------------------------
// BackgroundBlur::Initialize
//
// Loads the ONNX segmentation model via Windows ML and inspects its
// input/output tensor shapes to auto-configure preprocessing.
//----------------------------------------------------------------------------
bool BackgroundBlur::Initialize( const wchar_t* modelPath )
{
try
{
// Load the model from file.
m_model = winml::LearningModel::LoadFromFilePath( modelPath );
// Try GPU (DirectML) first for faster inference; fall back to CPU
// if no suitable GPU is available.
try
{
winml::LearningModelDevice gpuDevice( winml::LearningModelDeviceKind::DirectXHighPerformance );
m_session = winml::LearningModelSession( m_model, gpuDevice );
m_usingGpu = true;
OutputDebug( L"[BackgroundBlur] Using DirectML (GPU) for inference\n" );
}
catch( ... )
{
winml::LearningModelDevice cpuDevice( winml::LearningModelDeviceKind::Cpu );
m_session = winml::LearningModelSession( m_model, cpuDevice );
m_usingGpu = false;
OutputDebug( L"[BackgroundBlur] GPU unavailable, falling back to CPU\n" );
}
m_binding = winml::LearningModelBinding( m_session );
// Get input feature descriptor.
auto inputFeatures = m_model.InputFeatures();
if( inputFeatures.Size() == 0 )
{
OutputDebug( L"[BackgroundBlur] Model has no input features\n" );
return false;
}
auto inputDesc = inputFeatures.GetAt( 0 );
m_inputName = inputDesc.Name();
// Inspect input tensor shape.
auto tensorDesc = inputDesc.as<winml::ITensorFeatureDescriptor>();
auto shape = tensorDesc.Shape();
if( shape.Size() == 4 )
{
if( shape.GetAt( 1 ) == 3 || shape.GetAt( 1 ) == 1 )
{
// NCHW layout.
m_inputIsNchw = true;
m_modelInputChannels = shape.GetAt( 1 );
m_modelInputHeight = shape.GetAt( 2 ) > 0 ? shape.GetAt( 2 ) : 256;
m_modelInputWidth = shape.GetAt( 3 ) > 0 ? shape.GetAt( 3 ) : 256;
}
else
{
// NHWC layout.
m_inputIsNchw = false;
m_modelInputHeight = shape.GetAt( 1 ) > 0 ? shape.GetAt( 1 ) : 256;
m_modelInputWidth = shape.GetAt( 2 ) > 0 ? shape.GetAt( 2 ) : 256;
m_modelInputChannels = shape.GetAt( 3 );
}
}
// Get output feature name.
auto outputFeatures = m_model.OutputFeatures();
if( outputFeatures.Size() == 0 )
{
OutputDebug( L"[BackgroundBlur] Model has no output features\n" );
return false;
}
m_outputName = outputFeatures.GetAt( 0 ).Name();
OutputDebug( L"[BackgroundBlur] Model loaded: input=%s %lldx%lld (ch=%lld, %s)\n",
m_inputName.c_str(), m_modelInputWidth, m_modelInputHeight,
m_modelInputChannels, m_inputIsNchw ? L"NCHW" : L"NHWC" );
// Pre-allocate input tensor buffer.
m_inputTensor.resize( static_cast<size_t>( m_modelInputChannels * m_modelInputHeight * m_modelInputWidth ) );
return true;
}
catch( winrt::hresult_error const& ex )
{
OutputDebug( L"[BackgroundBlur] Initialize failed: %s (0x%08X)\n", ex.message().c_str(), ex.code().value );
m_session = nullptr;
m_model = nullptr;
return false;
}
}
//----------------------------------------------------------------------------
// BackgroundBlur::RunSegmentation
//
// Resizes the BGRA frame to the model's expected input size, converts
// to float RGB, runs inference via Windows ML, and produces a float mask
// in m_mask where 1.0 = person, 0.0 = background.
//----------------------------------------------------------------------------
bool BackgroundBlur::RunSegmentation( const uint8_t* bgraPixels, uint32_t width, uint32_t height,
bool modelResOnly )
{
const int64_t mW = m_modelInputWidth;
const int64_t mH = m_modelInputHeight;
const int64_t mC = m_modelInputChannels;
// Resize BGRA → model-sized float RGB using nearest-neighbor.
for( int64_t y = 0; y < mH; y++ )
{
uint32_t srcY = static_cast<uint32_t>( y * height / mH );
for( int64_t x = 0; x < mW; x++ )
{
uint32_t srcX = static_cast<uint32_t>( x * width / mW );
const uint8_t* px = bgraPixels + ( static_cast<size_t>( srcY ) * width + srcX ) * 4;
float b = px[0] / 255.0f;
float g = px[1] / 255.0f;
float r = px[2] / 255.0f;
if( m_inputIsNchw )
{
m_inputTensor[static_cast<size_t>(0ll * mH * mW + y * mW + x)] = r;
if( mC > 1 ) m_inputTensor[static_cast<size_t>(1ll * mH * mW + y * mW + x)] = g;
if( mC > 2 ) m_inputTensor[static_cast<size_t>(2ll * mH * mW + y * mW + x)] = b;
}
else
{
size_t idx = static_cast<size_t>( (y * mW + x) * mC );
m_inputTensor[idx + 0] = r;
if( mC > 1 ) m_inputTensor[idx + 1] = g;
if( mC > 2 ) m_inputTensor[idx + 2] = b;
}
}
}
try
{
// Create the input tensor shape.
std::vector<int64_t> inputShape;
if( m_inputIsNchw )
inputShape = { 1, mC, mH, mW };
else
inputShape = { 1, mH, mW, mC };
// Create a TensorFloat from our data.
auto inputTensor = winml::TensorFloat::CreateFromArray(
inputShape, winrt::array_view<const float>( m_inputTensor.data(),
m_inputTensor.data() + m_inputTensor.size() ) );
// Bind input and evaluate.
m_binding.Clear();
m_binding.Bind( m_inputName, inputTensor );
auto result = m_session.Evaluate( m_binding, L"" );
// Extract output tensor — bulk-copy to a raw float array so we
// avoid per-element WinRT/COM dispatch in the hot loop.
auto outputTensor = result.Outputs().Lookup( m_outputName ).as<winml::TensorFloat>();
auto outputShape = outputTensor.Shape();
auto outputView = outputTensor.GetAsVectorView();
const uint32_t outputSize = outputView.Size();
m_outputBuf.resize( outputSize );
outputView.GetMany( 0, m_outputBuf );
const float* outData = m_outputBuf.data();
// Determine output mask dimensions.
int64_t outH = mH, outW = mW;
int64_t numClasses = 1;
if( outputShape.Size() == 4 )
{
if( outputShape.GetAt( 1 ) <= 2 && outputShape.GetAt( 2 ) > 2 )
{
// [1, classes, H, W]
numClasses = outputShape.GetAt( 1 );
outH = outputShape.GetAt( 2 );
outW = outputShape.GetAt( 3 );
}
else
{
// [1, H, W, classes]
outH = outputShape.GetAt( 1 );
outW = outputShape.GetAt( 2 );
numClasses = outputShape.GetAt( 3 );
}
}
else if( outputShape.Size() == 3 )
{
outH = outputShape.GetAt( 1 );
outW = outputShape.GetAt( 2 );
}
// Store actual output dimensions for GetModelMaskWidth/Height.
m_modelOutputWidth = outW;
m_modelOutputHeight = outH;
// Build model-resolution mask first, apply sigmoid sharpening
// at model resolution (e.g. 256×256 = 65K pixels), then upscale
// to frame resolution.
const size_t modelPixels = static_cast<size_t>( outH * outW );
m_erodeBuf.resize( modelPixels );
// Extract person scores at model resolution from the raw array.
// Apply a hard threshold to produce a binary mask. This is much
// faster than a sigmoid (no expf) and eliminates the partial-blur
// halo that was bleeding onto body/head edges.
for( int64_t y = 0; y < outH; y++ )
{
for( int64_t x = 0; x < outW; x++ )
{
float personScore;
if( numClasses == 1 )
{
personScore = outData[y * outW + x];
}
else
{
float bg = outData[0 * outH * outW + y * outW + x];
float fg = outData[1 * outH * outW + y * outW + x];
personScore = ( fg > bg ) ? 1.0f : 0.0f;
}
m_erodeBuf[static_cast<size_t>( y * outW + x )] = ( personScore > 0.5f ) ? 1.0f : 0.0f;
}
}
// ── GPU path: model-resolution post-processing only ────────
// When modelResOnly is true, apply feathering and temporal
// smoothing at model resolution (e.g. 256×256 = 65K pixels)
// and return early. The GPU's hardware bilinear sampler will
// handle upscaling to frame resolution for free.
if( modelResOnly )
{
// Small box blur on m_erodeBuf for edge feathering.
// Radius 1 at 256×256 provides similar smoothing to
// radius 3 at 960×540 after bilinear upscale.
const int modelBlurRadius = 1;
const int modelDiam = modelBlurRadius * 2 + 1;
m_maskBlurBuf.resize( modelPixels );
// Horizontal pass.
for( int64_t y = 0; y < outH; y++ )
{
const float* srcRow = m_erodeBuf.data() + y * outW;
float* dstRow = m_maskBlurBuf.data() + y * outW;
float sum = 0.0f;
for( int i = -modelBlurRadius; i <= modelBlurRadius; i++ )
sum += srcRow[(std::max)( static_cast<int64_t>(0), (std::min)( outW - 1, static_cast<int64_t>( i ) ) )];
for( int64_t x = 0; x < outW; x++ )
{
dstRow[x] = sum / modelDiam;
int64_t remX = (std::max)( static_cast<int64_t>(0), x - modelBlurRadius );
int64_t addX = (std::min)( outW - 1, x + modelBlurRadius + 1 );
sum += srcRow[addX] - srcRow[remX];
}
}
// Vertical pass.
for( int64_t x = 0; x < outW; x++ )
{
float sum = 0.0f;
for( int i = -modelBlurRadius; i <= modelBlurRadius; i++ )
{
int64_t iy = (std::max)( static_cast<int64_t>(0), (std::min)( outH - 1, static_cast<int64_t>( i ) ) );
sum += m_maskBlurBuf[static_cast<size_t>( iy * outW + x )];
}
for( int64_t y = 0; y < outH; y++ )
{
m_erodeBuf[static_cast<size_t>( y * outW + x )] = sum / modelDiam;
int64_t remY = (std::max)( static_cast<int64_t>(0), y - modelBlurRadius );
int64_t addY = (std::min)( outH - 1, y + modelBlurRadius + 1 );
sum += m_maskBlurBuf[static_cast<size_t>( addY * outW + x )] -
m_maskBlurBuf[static_cast<size_t>( remY * outW + x )];
}
}
// Temporal smoothing at model resolution.
if( m_prevModelMask.size() == modelPixels )
{
constexpr float alpha = 0.6f;
constexpr float beta = 0.4f;
for( size_t i = 0; i < modelPixels; i++ )
m_erodeBuf[i] = alpha * m_erodeBuf[i] + beta * m_prevModelMask[i];
}
m_prevModelMask = m_erodeBuf;
return true;
}
// Upscale processed mask to frame dimensions via bilinear interpolation
// to produce smooth edges instead of staircase artifacts.
const size_t maskPixels = static_cast<size_t>( width ) * height;
m_mask.resize( maskPixels );
for( uint32_t y = 0; y < height; y++ )
{
float srcYf = ( y + 0.5f ) * outH / static_cast<float>( height ) - 0.5f;
srcYf = (std::max)( 0.0f, (std::min)( srcYf, static_cast<float>( outH - 1 ) ) );
int64_t y0 = static_cast<int64_t>( srcYf );
int64_t y1 = (std::min)( y0 + 1, outH - 1 );
float fy = srcYf - y0;
for( uint32_t x = 0; x < width; x++ )
{
float srcXf = ( x + 0.5f ) * outW / static_cast<float>( width ) - 0.5f;
srcXf = (std::max)( 0.0f, (std::min)( srcXf, static_cast<float>( outW - 1 ) ) );
int64_t x0 = static_cast<int64_t>( srcXf );
int64_t x1 = (std::min)( x0 + 1, outW - 1 );
float fx = srcXf - x0;
float v00 = m_erodeBuf[static_cast<size_t>(y0 * outW + x0)];
float v01 = m_erodeBuf[static_cast<size_t>(y0 * outW + x1)];
float v10 = m_erodeBuf[static_cast<size_t>(y1 * outW + x0)];
float v11 = m_erodeBuf[static_cast<size_t>(y1 * outW + x1)];
m_mask[static_cast<size_t>( y ) * width + x] =
v00 * ( 1.0f - fx ) * ( 1.0f - fy ) +
v01 * fx * ( 1.0f - fy ) +
v10 * ( 1.0f - fx ) * fy +
v11 * fx * fy;
}
}
// Apply a small box blur to the upscaled mask to feather edges.
const int maskBlurRadius = 3;
const int maskDiam = maskBlurRadius * 2 + 1;
m_maskBlurBuf.resize( maskPixels );
// Horizontal pass.
for( uint32_t y = 0; y < height; y++ )
{
const float* srcRow = m_mask.data() + static_cast<size_t>( y ) * width;
float* dstRow = m_maskBlurBuf.data() + static_cast<size_t>( y ) * width;
float sum = 0.0f;
for( int i = -maskBlurRadius; i <= maskBlurRadius; i++ )
sum += srcRow[(std::max)( 0, (std::min)( static_cast<int>( width ) - 1, i ) )];
for( uint32_t x = 0; x < width; x++ )
{
dstRow[x] = sum / maskDiam;
int remX = (std::max)( 0, static_cast<int>( x ) - maskBlurRadius );
int addX = (std::min)( static_cast<int>( width ) - 1, static_cast<int>( x ) + maskBlurRadius + 1 );
sum += srcRow[addX] - srcRow[remX];
}
}
// Vertical pass.
for( uint32_t x = 0; x < width; x++ )
{
float sum = 0.0f;
for( int i = -maskBlurRadius; i <= maskBlurRadius; i++ )
{
int iy = (std::max)( 0, (std::min)( static_cast<int>( height ) - 1, i ) );
sum += m_maskBlurBuf[static_cast<size_t>( iy ) * width + x];
}
for( uint32_t y = 0; y < height; y++ )
{
m_mask[static_cast<size_t>( y ) * width + x] = sum / maskDiam;
int remY = (std::max)( 0, static_cast<int>( y ) - maskBlurRadius );
int addY = (std::min)( static_cast<int>( height ) - 1, static_cast<int>( y ) + maskBlurRadius + 1 );
sum += m_maskBlurBuf[static_cast<size_t>( addY ) * width + x] -
m_maskBlurBuf[static_cast<size_t>( remY ) * width + x];
}
}
// Temporal smoothing: blend the current mask with the previous
// frame's mask to stabilize edges and reduce flicker. A weight
// of 0.6 current + 0.4 previous keeps edges responsive while
// dampening the frame-to-frame jitter around fine details like
// ears, hair, and fingers.
{
const size_t maskPixelsInner = static_cast<size_t>( width ) * height;
if( m_prevMask.size() == maskPixelsInner )
{
constexpr float alpha = 0.6f; // current frame weight
constexpr float beta = 0.4f; // previous frame weight
for( size_t i = 0; i < maskPixelsInner; i++ )
{
m_mask[i] = alpha * m_mask[i] + beta * m_prevMask[i];
}
}
m_prevMask = m_mask;
}
return true;
}
catch( winrt::hresult_error const& ex )
{
OutputDebug( L"[BackgroundBlur] Evaluate failed: %s (0x%08X)\n", ex.message().c_str(), ex.code().value );
return false;
}
}
//----------------------------------------------------------------------------
// HorizontalBoxBlur / VerticalBoxBlur
//
// Separable box blur passes used to build an approximate Gaussian.
//----------------------------------------------------------------------------
static void HorizontalBoxBlur(
const uint8_t* src, uint8_t* dst, uint32_t width, uint32_t height, int radius )
{
const int diameter = radius * 2 + 1;
for( uint32_t y = 0; y < height; y++ )
{
int rSum = 0, gSum = 0, bSum = 0;
const uint8_t* row = src + static_cast<size_t>( y ) * width * 4;
// Initialize window with clamped left edge.
for( int i = -radius; i <= radius; i++ )
{
int ix = (std::max)( 0, (std::min)( static_cast<int>( width ) - 1, i ) );
const uint8_t* px = row + ix * 4;
bSum += px[0];
gSum += px[1];
rSum += px[2];
}
uint8_t* dstRow = dst + static_cast<size_t>( y ) * width * 4;
for( uint32_t x = 0; x < width; x++ )
{
dstRow[x * 4 + 0] = static_cast<uint8_t>( bSum / diameter );
dstRow[x * 4 + 1] = static_cast<uint8_t>( gSum / diameter );
dstRow[x * 4 + 2] = static_cast<uint8_t>( rSum / diameter );
dstRow[x * 4 + 3] = 0xFF;
// Slide window: add right, remove left.
int removeX = (std::max)( 0, static_cast<int>( x ) - radius );
int addX = (std::min)( static_cast<int>( width ) - 1, static_cast<int>( x ) + radius + 1 );
const uint8_t* remPx = row + removeX * 4;
const uint8_t* addPx = row + addX * 4;
bSum += addPx[0] - remPx[0];
gSum += addPx[1] - remPx[1];
rSum += addPx[2] - remPx[2];
}
}
}
static void VerticalBoxBlur(
const uint8_t* src, uint8_t* dst, uint32_t width, uint32_t height, int radius )
{
const int diameter = radius * 2 + 1;
for( uint32_t x = 0; x < width; x++ )
{
int rSum = 0, gSum = 0, bSum = 0;
// Initialize window with clamped top edge.
for( int i = -radius; i <= radius; i++ )
{
int iy = (std::max)( 0, (std::min)( static_cast<int>( height ) - 1, i ) );
const uint8_t* px = src + ( static_cast<size_t>( iy ) * width + x ) * 4;
bSum += px[0];
gSum += px[1];
rSum += px[2];
}
for( uint32_t y = 0; y < height; y++ )
{
uint8_t* dstPx = dst + ( static_cast<size_t>( y ) * width + x ) * 4;
dstPx[0] = static_cast<uint8_t>( bSum / diameter );
dstPx[1] = static_cast<uint8_t>( gSum / diameter );
dstPx[2] = static_cast<uint8_t>( rSum / diameter );
dstPx[3] = 0xFF;
int removeY = (std::max)( 0, static_cast<int>( y ) - radius );
int addY = (std::min)( static_cast<int>( height ) - 1, static_cast<int>( y ) + radius + 1 );
const uint8_t* remPx = src + ( static_cast<size_t>( removeY ) * width + x ) * 4;
const uint8_t* addPx = src + ( static_cast<size_t>( addY ) * width + x ) * 4;
bSum += addPx[0] - remPx[0];
gSum += addPx[1] - remPx[1];
rSum += addPx[2] - remPx[2];
}
}
}
//----------------------------------------------------------------------------
// BackgroundBlur::ApplyBlurWithMask
//
// Downscales the frame to a small working size, blurs there, then
// performs a single full-resolution pass that blends the original
// pixels with the upscaled blurred pixels according to the mask.
//----------------------------------------------------------------------------
void BackgroundBlur::ApplyBlurWithMask( uint8_t* bgraPixels, uint32_t width, uint32_t height, int blurRadius )
{
const size_t frameBytes = static_cast<size_t>( width ) * height * 4;
m_blurredFrame.resize( frameBytes );
m_tempFrame.resize( frameBytes );
// The input is already capped at 960×540 by WebcamCapture, so blur
// directly — no need for a secondary downscale.
int effectiveRadius = (std::max)( 3, blurRadius );
// 2 iterations of box blur → approximate Gaussian.
HorizontalBoxBlur( bgraPixels, m_blurredFrame.data(), width, height, effectiveRadius );
VerticalBoxBlur( m_blurredFrame.data(), m_tempFrame.data(), width, height, effectiveRadius );
HorizontalBoxBlur( m_tempFrame.data(), m_blurredFrame.data(), width, height, effectiveRadius );
VerticalBoxBlur( m_blurredFrame.data(), m_tempFrame.data(), width, height, effectiveRadius );
// Blend pass with alpha support for smooth mask edges.
const uint8_t* blurData = m_tempFrame.data();
for( uint32_t y = 0; y < height; y++ )
{
uint8_t* dstRow = bgraPixels + static_cast<size_t>( y ) * width * 4;
const uint8_t* blurRow = blurData + static_cast<size_t>( y ) * width * 4;
const float* maskRow = m_mask.data() + static_cast<size_t>( y ) * width;
for( uint32_t x = 0; x < width; x++ )
{
float maskVal = maskRow[x];
// Fast path: fully person → keep original pixel untouched.
if( maskVal >= 1.0f )
continue;
uint8_t* dp = dstRow + x * 4;
const uint8_t* bp = blurRow + x * 4;
// Fast path: fully background → copy blurred pixel.
if( maskVal <= 0.0f )
{
*reinterpret_cast<uint32_t*>( dp ) = *reinterpret_cast<const uint32_t*>( bp );
continue;
}
// Edge pixel → alpha blend original and blurred.
float inv = 1.0f - maskVal;
dp[0] = static_cast<uint8_t>( dp[0] * maskVal + bp[0] * inv + 0.5f );
dp[1] = static_cast<uint8_t>( dp[1] * maskVal + bp[1] * inv + 0.5f );
dp[2] = static_cast<uint8_t>( dp[2] * maskVal + bp[2] * inv + 0.5f );
}
}
}
//----------------------------------------------------------------------------
// BackgroundBlur::SetBackgroundImage
//
// Loads an image file via WIC and stores it as a BGRA pixel buffer.
//----------------------------------------------------------------------------
bool BackgroundBlur::SetBackgroundImage( const wchar_t* imagePath )
{
m_bgImage.clear();
m_bgImageWidth = 0;
m_bgImageHeight = 0;
m_scaledBgImage.clear();
m_scaledBgW = 0;
m_scaledBgH = 0;
if( !imagePath || !*imagePath )
return false;
auto factory = wil::CoCreateInstance<IWICImagingFactory>( CLSID_WICImagingFactory );
if( !factory )
return false;
wil::com_ptr<IWICBitmapDecoder> decoder;
HRESULT hr = factory->CreateDecoderFromFilename(
imagePath, nullptr, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &decoder );
if( FAILED( hr ) )
{
OutputDebug( L"[BackgroundBlur] Failed to decode image: %s (hr=0x%08X)\n", imagePath, hr );
return false;
}
wil::com_ptr<IWICBitmapFrameDecode> frame;
hr = decoder->GetFrame( 0, &frame );
if( FAILED( hr ) )
return false;
// Convert to BGRA 32bpp.
wil::com_ptr<IWICFormatConverter> converter;
hr = factory->CreateFormatConverter( &converter );
if( FAILED( hr ) )
return false;
hr = converter->Initialize(
frame.get(), GUID_WICPixelFormat32bppBGRA,
WICBitmapDitherTypeNone, nullptr, 0.0, WICBitmapPaletteTypeCustom );
if( FAILED( hr ) )
return false;
UINT w = 0, h = 0;
converter->GetSize( &w, &h );
if( w == 0 || h == 0 )
return false;
m_bgImage.resize( static_cast<size_t>( w ) * h * 4 );
hr = converter->CopyPixels( nullptr, w * 4, static_cast<UINT>( m_bgImage.size() ), m_bgImage.data() );
if( FAILED( hr ) )
{
m_bgImage.clear();
return false;
}
m_bgImageWidth = w;
m_bgImageHeight = h;
OutputDebug( L"[BackgroundBlur] Background image loaded: %ux%u from %s\n", w, h, imagePath );
return true;
}
//----------------------------------------------------------------------------
// BackgroundBlur::EnsureScaledBgImage
//
// Scales the loaded background image to the specified dimensions using
// nearest-neighbor. The result is cached and only recomputed when the
// target dimensions change. The image is center-cropped to preserve
// aspect ratio (like "cover" scaling).
//----------------------------------------------------------------------------
void BackgroundBlur::EnsureScaledBgImage( uint32_t width, uint32_t height )
{
if( m_scaledBgW == width && m_scaledBgH == height && !m_scaledBgImage.empty() )
return;
m_scaledBgImage.resize( static_cast<size_t>( width ) * height * 4 );
m_scaledBgW = width;
m_scaledBgH = height;
// Compute center-crop of the source image to match the target aspect ratio.
double targetAspect = static_cast<double>( width ) / height;
double srcAspect = static_cast<double>( m_bgImageWidth ) / m_bgImageHeight;
uint32_t cropW, cropH, cropX, cropY;
if( srcAspect > targetAspect )
{
// Source is wider — crop horizontally.
cropH = m_bgImageHeight;
cropW = static_cast<uint32_t>( m_bgImageHeight * targetAspect + 0.5 );
cropX = ( m_bgImageWidth - cropW ) / 2;
cropY = 0;
}
else
{
// Source is taller — crop vertically.
cropW = m_bgImageWidth;
cropH = static_cast<uint32_t>( m_bgImageWidth / targetAspect + 0.5 );
cropX = 0;
cropY = ( m_bgImageHeight - cropH ) / 2;
}
for( uint32_t y = 0; y < height; y++ )
{
uint32_t srcY = cropY + y * cropH / height;
for( uint32_t x = 0; x < width; x++ )
{
uint32_t srcX = cropX + x * cropW / width;
size_t srcIdx = ( static_cast<size_t>( srcY ) * m_bgImageWidth + srcX ) * 4;
size_t dstIdx = ( static_cast<size_t>( y ) * width + x ) * 4;
m_scaledBgImage[dstIdx + 0] = m_bgImage[srcIdx + 0];
m_scaledBgImage[dstIdx + 1] = m_bgImage[srcIdx + 1];
m_scaledBgImage[dstIdx + 2] = m_bgImage[srcIdx + 2];
m_scaledBgImage[dstIdx + 3] = 0xFF;
}
}
}
//----------------------------------------------------------------------------
// BackgroundBlur::ApplyImageWithMask
//
// Replaces background pixels with the loaded background image using the
// segmentation mask. Person pixels are preserved, background pixels come
// from the scaled image.
//----------------------------------------------------------------------------
void BackgroundBlur::ApplyImageWithMask( uint8_t* bgraPixels, uint32_t width, uint32_t height )
{
EnsureScaledBgImage( width, height );
const uint8_t* bgData = m_scaledBgImage.data();
for( uint32_t y = 0; y < height; y++ )
{
uint8_t* dstRow = bgraPixels + static_cast<size_t>( y ) * width * 4;
const uint8_t* bgRow = bgData + static_cast<size_t>( y ) * width * 4;
const float* maskRow = m_mask.data() + static_cast<size_t>( y ) * width;
for( uint32_t x = 0; x < width; x++ )
{
float maskVal = maskRow[x];
// Fully person → keep original pixel.
if( maskVal >= 1.0f )
continue;
uint8_t* dp = dstRow + x * 4;
const uint8_t* bp = bgRow + x * 4;
// Fully background → copy background image pixel.
if( maskVal <= 0.0f )
{
*reinterpret_cast<uint32_t*>( dp ) = *reinterpret_cast<const uint32_t*>( bp );
continue;
}
// Edge pixel → alpha blend person and background image.
float inv = 1.0f - maskVal;
dp[0] = static_cast<uint8_t>( dp[0] * maskVal + bp[0] * inv + 0.5f );
dp[1] = static_cast<uint8_t>( dp[1] * maskVal + bp[1] * inv + 0.5f );
dp[2] = static_cast<uint8_t>( dp[2] * maskVal + bp[2] * inv + 0.5f );
}
}
}
//----------------------------------------------------------------------------
// BackgroundBlur::ShouldRunInference
//
// Decides whether segmentation inference should run this frame.
// Uses a combination of periodic fallback and motion detection:
// motion is estimated by comparing luminance at a sparse grid of
// sample points with the previous frame. When the scene changes
// quickly (fast head movement), inference runs every frame.
//----------------------------------------------------------------------------
bool BackgroundBlur::ShouldRunInference( const uint8_t* bgraPixels, uint32_t width, uint32_t height )
{
// Always run if no cached mask or dimensions changed.
if( !m_hasCachedMask || m_lastMaskWidth != width || m_lastMaskHeight != height )
return true;
// Periodic fallback: run at least every N frames.
const uint32_t pixels = width * height;
const int inferenceInterval = ( pixels > 500000 ) ? 6 : 3;
if( ( m_frameCounter % inferenceInterval ) == 0 )
return true;
// Motion detection: sample luminance at a sparse grid and compare
// with the previous frame.
constexpr int gridSize = MOTION_GRID_SIZE;
constexpr int numSamples = gridSize * gridSize;
float curSamples[numSamples];
for( int gy = 0; gy < gridSize; gy++ )
{
uint32_t sy = ( gy * 2 + 1 ) * height / ( gridSize * 2 );
for( int gx = 0; gx < gridSize; gx++ )
{
uint32_t sx = ( gx * 2 + 1 ) * width / ( gridSize * 2 );
const uint8_t* px = bgraPixels + ( static_cast<size_t>( sy ) * width + sx ) * 4;
curSamples[gy * gridSize + gx] = 0.299f * px[2] + 0.587f * px[1] + 0.114f * px[0];
}
}
float motionScore = 0.0f;
if( m_hasPrevSamples )
{
for( int i = 0; i < numSamples; i++ )
{
float diff = curSamples[i] - m_prevSamples[i];
motionScore += diff > 0.0f ? diff : -diff;
}
motionScore /= numSamples;
}
memcpy( m_prevSamples, curSamples, sizeof( curSamples ) );
m_hasPrevSamples = true;
// Average per-sample luminance change > 5/255 indicates significant motion.
return motionScore > 5.0f;
}
//----------------------------------------------------------------------------
// BackgroundBlur::ApplyImageReplacement
//
// Main entry point for background image replacement mode.
//----------------------------------------------------------------------------
bool BackgroundBlur::ApplyImageReplacement( uint8_t* bgraPixels, uint32_t width, uint32_t height )
{
if( !m_session || !bgraPixels || width == 0 || height == 0 )
return false;
if( m_bgImage.empty() )
return false;
if( ShouldRunInference( bgraPixels, width, height ) )
{
if( !RunSegmentation( bgraPixels, width, height ) )
return false;
m_lastMaskWidth = width;
m_lastMaskHeight = height;
m_hasCachedMask = true;
}
m_frameCounter++;
ApplyImageWithMask( bgraPixels, width, height );
return true;
}
//----------------------------------------------------------------------------
// BackgroundBlur::Apply
//
// Main entry point: runs segmentation and applies blur to the background.
//----------------------------------------------------------------------------
bool BackgroundBlur::Apply( uint8_t* bgraPixels, uint32_t width, uint32_t height, int blurRadius )
{
if( !m_session || !bgraPixels || width == 0 || height == 0 )
return false;
if( ShouldRunInference( bgraPixels, width, height ) )
{
if( !RunSegmentation( bgraPixels, width, height ) )
return false;
m_lastMaskWidth = width;
m_lastMaskHeight = height;
m_hasCachedMask = true;
}
m_frameCounter++;
ApplyBlurWithMask( bgraPixels, width, height, blurRadius );
return true;
}
//----------------------------------------------------------------------------
// BackgroundBlur::RunSegmentationOnly
//
// Runs the segmentation model and produces the mask, but does NOT blur
// or modify the pixel buffer. Used when the GPU compute shader will
// perform the box blur instead of the CPU.
//----------------------------------------------------------------------------
bool BackgroundBlur::RunSegmentationOnly( const uint8_t* bgraPixels, uint32_t width, uint32_t height )
{
if( !m_session || !bgraPixels || width == 0 || height == 0 )
return false;
if( ShouldRunInference( bgraPixels, width, height ) )
{
// Model-resolution only: skip CPU upscale+feather at frame
// resolution — the GPU bilinear sampler handles that.
if( !RunSegmentation( bgraPixels, width, height, /*modelResOnly=*/ true ) )
return false;
m_lastMaskWidth = width;
m_lastMaskHeight = height;
m_hasCachedMask = true;
}
m_frameCounter++;
return m_hasCachedMask;
}

View File

@@ -1,160 +0,0 @@
//==============================================================================
//
// BackgroundBlur.h
//
// Performs person segmentation using Windows ML (Windows.AI.MachineLearning)
// and applies either a Gaussian blur or a custom background image to the
// background of a BGRA webcam frame. The segmentation model runs on CPU
// via the Windows ML default device, keeping the GPU free for recording.
//
// Copyright (C) Mark Russinovich
// Sysinternals - www.sysinternals.com
//
//==============================================================================
#pragma once
#include <vector>
#include <string>
#include <cstdint>
#include <winrt/Windows.AI.MachineLearning.h>
// Background processing mode for the webcam overlay.
enum class WebcamBackgroundMode : uint32_t
{
None = 0, // No background processing
Blur = 1, // Gaussian blur on the background
Image = 2, // Replace background with a user-chosen image
};
class BackgroundBlur
{
public:
BackgroundBlur() = default;
~BackgroundBlur() = default;
// Initialize the ONNX model. modelPath must point to a valid .onnx
// segmentation model file. Returns true on success.
bool Initialize( const wchar_t* modelPath );
// Load a background replacement image from the given file path.
// The image is decoded via WIC and stored as a BGRA buffer.
// Returns true on success.
bool SetBackgroundImage( const wchar_t* imagePath );
// Returns true if a background image has been loaded.
bool HasBackgroundImage() const { return !m_bgImage.empty(); }
// Apply background blur to a BGRA pixel buffer in-place.
// width/height are the frame dimensions. blurRadius controls
// the strength of the Gaussian blur (in pixels).
// Returns true if segmentation + blur was applied successfully.
bool Apply( uint8_t* bgraPixels, uint32_t width, uint32_t height, int blurRadius = 21 );
// Apply background image replacement to a BGRA pixel buffer in-place.
// Uses the previously loaded background image (via SetBackgroundImage).
// Returns true if segmentation + image replacement was applied.
bool ApplyImageReplacement( uint8_t* bgraPixels, uint32_t width, uint32_t height );
// Returns true if the model has been loaded successfully.
bool IsInitialized() const { return m_session != nullptr; }
// Access the segmentation mask after Apply()/ApplyImageReplacement().
// The mask has one float [0..1] per pixel at the processing resolution
// (1.0 = person / foreground, 0.0 = background).
const std::vector<float>& GetMask() const { return m_mask; }
uint32_t GetMaskWidth() const { return m_lastMaskWidth; }
uint32_t GetMaskHeight() const { return m_lastMaskHeight; }
bool HasCachedMask() const { return m_hasCachedMask; }
// Run segmentation only (no CPU blur or mask blend). Use this when
// the blur will be performed on the GPU via a compute shader.
// Populates the mask (GetMask) but does NOT touch bgraPixels.
bool RunSegmentationOnly( const uint8_t* bgraPixels, uint32_t width, uint32_t height );
// Access the fully-blurred frame after Apply().
// Contains all pixels blurred (before mask-based compositing).
// Only valid after Apply() — NOT after ApplyImageReplacement().
const std::vector<uint8_t>& GetBlurredFrame() const { return m_tempFrame; }
// Access the model-resolution mask before upscaling (e.g. 256×256).
// Useful when the GPU handles upscaling via hardware bilinear filtering.
const std::vector<float>& GetModelMask() const { return m_erodeBuf; }
int64_t GetModelMaskWidth() const { return m_modelOutputWidth; }
int64_t GetModelMaskHeight() const { return m_modelOutputHeight; }
private:
// Run the segmentation model and produce a float mask [0..1] per pixel.
// When modelResOnly is true, stops after model-resolution post-processing
// (feathering + temporal smoothing at 256×256) and skips the CPU upscale
// to frame resolution — the GPU bilinear sampler handles that instead.
bool RunSegmentation( const uint8_t* bgraPixels, uint32_t width, uint32_t height,
bool modelResOnly = false );
// Apply box blur (iterated for Gaussian approximation) to bgraPixels
// only where the mask indicates background.
void ApplyBlurWithMask( uint8_t* bgraPixels, uint32_t width, uint32_t height, int blurRadius );
// Replace background pixels with the loaded background image.
void ApplyImageWithMask( uint8_t* bgraPixels, uint32_t width, uint32_t height );
// Scale the loaded background image to the given dimensions (cached).
void EnsureScaledBgImage( uint32_t width, uint32_t height );
// Decide whether inference is needed this frame (periodic + motion-adaptive).
bool ShouldRunInference( const uint8_t* bgraPixels, uint32_t width, uint32_t height );
// Windows ML objects.
winrt::Windows::AI::MachineLearning::LearningModel m_model{ nullptr };
winrt::Windows::AI::MachineLearning::LearningModelSession m_session{ nullptr };
winrt::Windows::AI::MachineLearning::LearningModelBinding m_binding{ nullptr };
winrt::hstring m_inputName;
winrt::hstring m_outputName;
// Model metadata (detected from the loaded model).
int64_t m_modelInputWidth = 256;
int64_t m_modelInputHeight = 256;
int64_t m_modelInputChannels = 3;
bool m_inputIsNchw = true; // true = [1,C,H,W], false = [1,H,W,C]
bool m_usingGpu = false; // true if DirectML session is active
// Actual model output dimensions (may differ from input dimensions).
int64_t m_modelOutputWidth = 256;
int64_t m_modelOutputHeight = 256;
// Reusable buffers to avoid per-frame allocations.
std::vector<float> m_inputTensor; // RGB float [1,3,H,W] or [1,H,W,3]
std::vector<float> m_outputBuf; // Raw copy of output tensor data
std::vector<float> m_mask; // Segmentation mask [width*height]
std::vector<float> m_erodeBuf; // Model-resolution mask buffer
std::vector<float> m_maskBlurBuf; // Temp buffer for mask edge smoothing
std::vector<uint8_t> m_blurredFrame; // Temporary blurred copy
std::vector<uint8_t> m_tempFrame; // Second temp buffer for blur passes
// Background image (original resolution, BGRA).
std::vector<uint8_t> m_bgImage;
uint32_t m_bgImageWidth = 0;
uint32_t m_bgImageHeight = 0;
// Scaled background image (cached at overlay dimensions).
std::vector<uint8_t> m_scaledBgImage;
uint32_t m_scaledBgW = 0;
uint32_t m_scaledBgH = 0;
// Frame-skipping: reuse the segmentation mask for N frames.
int m_frameCounter = 0;
uint32_t m_lastMaskWidth = 0;
uint32_t m_lastMaskHeight = 0;
bool m_hasCachedMask = false;
// Motion detection: luminance samples from the previous frame.
static constexpr int MOTION_GRID_SIZE = 8; // 8×8 = 64 sample points
float m_prevSamples[MOTION_GRID_SIZE * MOTION_GRID_SIZE] = {};
bool m_hasPrevSamples = false;
// Temporal smoothing: previous frame's mask blended with current
// to stabilize edges and reduce flicker.
std::vector<float> m_prevMask;
// Model-resolution previous mask for GPU path temporal smoothing.
std::vector<float> m_prevModelMask;
};

View File

@@ -1,606 +0,0 @@
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 10.1
//
//
// Buffer Definitions:
//
// cbuffer BlurConstants
// {
//
// uint Direction; // Offset: 0 Size: 4
// int Radius; // Offset: 4 Size: 4
// uint Width; // Offset: 8 Size: 4
// uint Height; // Offset: 12 Size: 4
//
// }
//
//
// Resource Bindings:
//
// Name Type Format Dim HLSL Bind Count
// ------------------------------ ---------- ------- ----------- -------------- ------
// InputTex texture float4 2d t0 1
// OutputTex UAV float4 2d u0 1
// BlurConstants cbuffer NA NA cb0 1
//
//
//
// Input signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// no Input
//
// Output signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// no Output
cs_5_0
dcl_globalFlags refactoringAllowed
dcl_constantbuffer CB0[1], immediateIndexed
dcl_resource_texture2d (float,float,float,float) t0
dcl_uav_typed_texture2d (float,float,float,float) u0
dcl_input vThreadGroupID.xy
dcl_input vThreadIDInGroup.x
dcl_temps 4
dcl_tgsm_structured g0, 16, 320
dcl_thread_group 256, 1, 1
imin r0.x, cb0[0].y, l(32)
ishl r0.y, r0.x, l(1)
iadd r0.z, r0.y, l(256)
if_z cb0[0].x
uge r0.w, vThreadGroupID.y, cb0[0].w
if_nz r0.w
ret
endif
ishl r0.w, vThreadGroupID.x, l(8)
iadd r1.x, cb0[0].z, l(-1)
mov r2.y, vThreadGroupID.y
mov r2.zw, l(0,0,0,0)
mov r1.y, vThreadIDInGroup.x
loop
ige r1.z, r1.y, r0.z
breakc_nz r1.z
iadd r1.z, r0.w, r1.y
iadd r1.z, -r0.x, r1.z
imax r1.z, r1.z, l(0)
imin r2.x, r1.x, r1.z
ld_indexable(texture2d)(float,float,float,float) r3.xyzw, r2.xyzw, t0.xyzw
store_structured g0.xyzw, r1.y, l(0), r3.xyzw
iadd r1.y, r1.y, l(256)
endloop
sync_g_t
imad r1.x, vThreadGroupID.x, l(256), vThreadIDInGroup.x
uge r0.w, r1.x, cb0[0].z
if_nz r0.w
ret
endif
mov r2.xyzw, l(0,0,0,0)
mov r0.w, l(0)
loop
ilt r3.x, r0.y, r0.w
breakc_nz r3.x
iadd r3.x, r0.w, vThreadIDInGroup.x
ld_structured r3.xyzw, r3.x, l(0), g0.xyzw
add r2.xyzw, r2.xyzw, r3.xyzw
iadd r0.w, r0.w, l(1)
endloop
bfi r0.w, l(31), l(1), r0.x, l(1)
itof r0.w, r0.w
div r2.xyzw, r2.xyzw, r0.wwww
mov r1.yzw, vThreadGroupID.yyyy
store_uav_typed u0.xyzw, r1.xyzw, r2.xyzw
else
uge r0.w, vThreadGroupID.y, cb0[0].z
if_nz r0.w
ret
endif
ishl r0.w, vThreadGroupID.x, l(8)
iadd r1.x, cb0[0].w, l(-1)
mov r2.x, vThreadGroupID.y
mov r2.zw, l(0,0,0,0)
mov r1.y, vThreadIDInGroup.x
loop
ige r1.z, r1.y, r0.z
breakc_nz r1.z
iadd r1.z, r0.w, r1.y
iadd r1.z, -r0.x, r1.z
imax r1.z, r1.z, l(0)
imin r2.y, r1.x, r1.z
ld_indexable(texture2d)(float,float,float,float) r3.xyzw, r2.xyzw, t0.xyzw
store_structured g0.xyzw, r1.y, l(0), r3.xyzw
iadd r1.y, r1.y, l(256)
endloop
sync_g_t
imad r1.yzw, vThreadGroupID.xxxx, l(0, 256, 256, 256), vThreadIDInGroup.xxxx
uge r0.z, r1.w, cb0[0].w
if_nz r0.z
ret
endif
mov r2.xyzw, l(0,0,0,0)
mov r0.z, l(0)
loop
ilt r0.w, r0.y, r0.z
breakc_nz r0.w
iadd r0.w, r0.z, vThreadIDInGroup.x
ld_structured r3.xyzw, r0.w, l(0), g0.xyzw
add r2.xyzw, r2.xyzw, r3.xyzw
iadd r0.z, r0.z, l(1)
endloop
bfi r0.x, l(31), l(1), r0.x, l(1)
itof r0.x, r0.x
div r0.xyzw, r2.xyzw, r0.xxxx
mov r1.x, vThreadGroupID.y
store_uav_typed u0.xyzw, r1.xyzw, r0.xyzw
endif
ret
// Approximately 89 instruction slots used
#endif
const BYTE g_BoxBlurCS[] =
{
68, 88, 66, 67, 109, 156,
172, 79, 78, 198, 69, 61,
87, 5, 27, 232, 85, 229,
69, 181, 1, 0, 0, 0,
212, 10, 0, 0, 5, 0,
0, 0, 52, 0, 0, 0,
80, 2, 0, 0, 96, 2,
0, 0, 112, 2, 0, 0,
56, 10, 0, 0, 82, 68,
69, 70, 20, 2, 0, 0,
1, 0, 0, 0, 192, 0,
0, 0, 3, 0, 0, 0,
60, 0, 0, 0, 0, 5,
83, 67, 0, 1, 0, 0,
233, 1, 0, 0, 82, 68,
49, 49, 60, 0, 0, 0,
24, 0, 0, 0, 32, 0,
0, 0, 40, 0, 0, 0,
36, 0, 0, 0, 12, 0,
0, 0, 0, 0, 0, 0,
156, 0, 0, 0, 2, 0,
0, 0, 5, 0, 0, 0,
4, 0, 0, 0, 255, 255,
255, 255, 0, 0, 0, 0,
1, 0, 0, 0, 13, 0,
0, 0, 165, 0, 0, 0,
4, 0, 0, 0, 5, 0,
0, 0, 4, 0, 0, 0,
255, 255, 255, 255, 0, 0,
0, 0, 1, 0, 0, 0,
13, 0, 0, 0, 175, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0,
0, 0, 1, 0, 0, 0,
73, 110, 112, 117, 116, 84,
101, 120, 0, 79, 117, 116,
112, 117, 116, 84, 101, 120,
0, 66, 108, 117, 114, 67,
111, 110, 115, 116, 97, 110,
116, 115, 0, 171, 171, 171,
175, 0, 0, 0, 4, 0,
0, 0, 216, 0, 0, 0,
16, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
120, 1, 0, 0, 0, 0,
0, 0, 4, 0, 0, 0,
2, 0, 0, 0, 136, 1,
0, 0, 0, 0, 0, 0,
255, 255, 255, 255, 0, 0,
0, 0, 255, 255, 255, 255,
0, 0, 0, 0, 172, 1,
0, 0, 4, 0, 0, 0,
4, 0, 0, 0, 2, 0,
0, 0, 184, 1, 0, 0,
0, 0, 0, 0, 255, 255,
255, 255, 0, 0, 0, 0,
255, 255, 255, 255, 0, 0,
0, 0, 220, 1, 0, 0,
8, 0, 0, 0, 4, 0,
0, 0, 2, 0, 0, 0,
136, 1, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255,
0, 0, 0, 0, 255, 255,
255, 255, 0, 0, 0, 0,
226, 1, 0, 0, 12, 0,
0, 0, 4, 0, 0, 0,
2, 0, 0, 0, 136, 1,
0, 0, 0, 0, 0, 0,
255, 255, 255, 255, 0, 0,
0, 0, 255, 255, 255, 255,
0, 0, 0, 0, 68, 105,
114, 101, 99, 116, 105, 111,
110, 0, 100, 119, 111, 114,
100, 0, 0, 0, 19, 0,
1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 130, 1,
0, 0, 82, 97, 100, 105,
117, 115, 0, 105, 110, 116,
0, 171, 0, 0, 2, 0,
1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 179, 1,
0, 0, 87, 105, 100, 116,
104, 0, 72, 101, 105, 103,
104, 116, 0, 77, 105, 99,
114, 111, 115, 111, 102, 116,
32, 40, 82, 41, 32, 72,
76, 83, 76, 32, 83, 104,
97, 100, 101, 114, 32, 67,
111, 109, 112, 105, 108, 101,
114, 32, 49, 48, 46, 49,
0, 171, 171, 171, 73, 83,
71, 78, 8, 0, 0, 0,
0, 0, 0, 0, 8, 0,
0, 0, 79, 83, 71, 78,
8, 0, 0, 0, 0, 0,
0, 0, 8, 0, 0, 0,
83, 72, 69, 88, 192, 7,
0, 0, 80, 0, 5, 0,
240, 1, 0, 0, 106, 8,
0, 1, 89, 0, 0, 4,
70, 142, 32, 0, 0, 0,
0, 0, 1, 0, 0, 0,
88, 24, 0, 4, 0, 112,
16, 0, 0, 0, 0, 0,
85, 85, 0, 0, 156, 24,
0, 4, 0, 224, 17, 0,
0, 0, 0, 0, 85, 85,
0, 0, 95, 0, 0, 2,
50, 16, 2, 0, 95, 0,
0, 2, 18, 32, 2, 0,
104, 0, 0, 2, 4, 0,
0, 0, 160, 0, 0, 5,
0, 240, 17, 0, 0, 0,
0, 0, 16, 0, 0, 0,
64, 1, 0, 0, 155, 0,
0, 4, 0, 1, 0, 0,
1, 0, 0, 0, 1, 0,
0, 0, 37, 0, 0, 8,
18, 0, 16, 0, 0, 0,
0, 0, 26, 128, 32, 0,
0, 0, 0, 0, 0, 0,
0, 0, 1, 64, 0, 0,
32, 0, 0, 0, 41, 0,
0, 7, 34, 0, 16, 0,
0, 0, 0, 0, 10, 0,
16, 0, 0, 0, 0, 0,
1, 64, 0, 0, 1, 0,
0, 0, 30, 0, 0, 7,
66, 0, 16, 0, 0, 0,
0, 0, 26, 0, 16, 0,
0, 0, 0, 0, 1, 64,
0, 0, 0, 1, 0, 0,
31, 0, 0, 4, 10, 128,
32, 0, 0, 0, 0, 0,
0, 0, 0, 0, 80, 0,
0, 7, 130, 0, 16, 0,
0, 0, 0, 0, 26, 16,
2, 0, 58, 128, 32, 0,
0, 0, 0, 0, 0, 0,
0, 0, 31, 0, 4, 3,
58, 0, 16, 0, 0, 0,
0, 0, 62, 0, 0, 1,
21, 0, 0, 1, 41, 0,
0, 6, 130, 0, 16, 0,
0, 0, 0, 0, 10, 16,
2, 0, 1, 64, 0, 0,
8, 0, 0, 0, 30, 0,
0, 8, 18, 0, 16, 0,
1, 0, 0, 0, 42, 128,
32, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 64,
0, 0, 255, 255, 255, 255,
54, 0, 0, 4, 34, 0,
16, 0, 2, 0, 0, 0,
26, 16, 2, 0, 54, 0,
0, 8, 194, 0, 16, 0,
2, 0, 0, 0, 2, 64,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
54, 0, 0, 4, 34, 0,
16, 0, 1, 0, 0, 0,
10, 32, 2, 0, 48, 0,
0, 1, 33, 0, 0, 7,
66, 0, 16, 0, 1, 0,
0, 0, 26, 0, 16, 0,
1, 0, 0, 0, 42, 0,
16, 0, 0, 0, 0, 0,
3, 0, 4, 3, 42, 0,
16, 0, 1, 0, 0, 0,
30, 0, 0, 7, 66, 0,
16, 0, 1, 0, 0, 0,
58, 0, 16, 0, 0, 0,
0, 0, 26, 0, 16, 0,
1, 0, 0, 0, 30, 0,
0, 8, 66, 0, 16, 0,
1, 0, 0, 0, 10, 0,
16, 128, 65, 0, 0, 0,
0, 0, 0, 0, 42, 0,
16, 0, 1, 0, 0, 0,
36, 0, 0, 7, 66, 0,
16, 0, 1, 0, 0, 0,
42, 0, 16, 0, 1, 0,
0, 0, 1, 64, 0, 0,
0, 0, 0, 0, 37, 0,
0, 7, 18, 0, 16, 0,
2, 0, 0, 0, 10, 0,
16, 0, 1, 0, 0, 0,
42, 0, 16, 0, 1, 0,
0, 0, 45, 0, 0, 137,
194, 0, 0, 128, 67, 85,
21, 0, 242, 0, 16, 0,
3, 0, 0, 0, 70, 14,
16, 0, 2, 0, 0, 0,
70, 126, 16, 0, 0, 0,
0, 0, 168, 0, 0, 9,
242, 240, 17, 0, 0, 0,
0, 0, 26, 0, 16, 0,
1, 0, 0, 0, 1, 64,
0, 0, 0, 0, 0, 0,
70, 14, 16, 0, 3, 0,
0, 0, 30, 0, 0, 7,
34, 0, 16, 0, 1, 0,
0, 0, 26, 0, 16, 0,
1, 0, 0, 0, 1, 64,
0, 0, 0, 1, 0, 0,
22, 0, 0, 1, 190, 24,
0, 1, 35, 0, 0, 7,
18, 0, 16, 0, 1, 0,
0, 0, 10, 16, 2, 0,
1, 64, 0, 0, 0, 1,
0, 0, 10, 32, 2, 0,
80, 0, 0, 8, 130, 0,
16, 0, 0, 0, 0, 0,
10, 0, 16, 0, 1, 0,
0, 0, 42, 128, 32, 0,
0, 0, 0, 0, 0, 0,
0, 0, 31, 0, 4, 3,
58, 0, 16, 0, 0, 0,
0, 0, 62, 0, 0, 1,
21, 0, 0, 1, 54, 0,
0, 8, 242, 0, 16, 0,
2, 0, 0, 0, 2, 64,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
54, 0, 0, 5, 130, 0,
16, 0, 0, 0, 0, 0,
1, 64, 0, 0, 0, 0,
0, 0, 48, 0, 0, 1,
34, 0, 0, 7, 18, 0,
16, 0, 3, 0, 0, 0,
26, 0, 16, 0, 0, 0,
0, 0, 58, 0, 16, 0,
0, 0, 0, 0, 3, 0,
4, 3, 10, 0, 16, 0,
3, 0, 0, 0, 30, 0,
0, 6, 18, 0, 16, 0,
3, 0, 0, 0, 58, 0,
16, 0, 0, 0, 0, 0,
10, 32, 2, 0, 167, 0,
0, 9, 242, 0, 16, 0,
3, 0, 0, 0, 10, 0,
16, 0, 3, 0, 0, 0,
1, 64, 0, 0, 0, 0,
0, 0, 70, 254, 17, 0,
0, 0, 0, 0, 0, 0,
0, 7, 242, 0, 16, 0,
2, 0, 0, 0, 70, 14,
16, 0, 2, 0, 0, 0,
70, 14, 16, 0, 3, 0,
0, 0, 30, 0, 0, 7,
130, 0, 16, 0, 0, 0,
0, 0, 58, 0, 16, 0,
0, 0, 0, 0, 1, 64,
0, 0, 1, 0, 0, 0,
22, 0, 0, 1, 140, 0,
0, 11, 130, 0, 16, 0,
0, 0, 0, 0, 1, 64,
0, 0, 31, 0, 0, 0,
1, 64, 0, 0, 1, 0,
0, 0, 10, 0, 16, 0,
0, 0, 0, 0, 1, 64,
0, 0, 1, 0, 0, 0,
43, 0, 0, 5, 130, 0,
16, 0, 0, 0, 0, 0,
58, 0, 16, 0, 0, 0,
0, 0, 14, 0, 0, 7,
242, 0, 16, 0, 2, 0,
0, 0, 70, 14, 16, 0,
2, 0, 0, 0, 246, 15,
16, 0, 0, 0, 0, 0,
54, 0, 0, 4, 226, 0,
16, 0, 1, 0, 0, 0,
86, 21, 2, 0, 164, 0,
0, 7, 242, 224, 17, 0,
0, 0, 0, 0, 70, 14,
16, 0, 1, 0, 0, 0,
70, 14, 16, 0, 2, 0,
0, 0, 18, 0, 0, 1,
80, 0, 0, 7, 130, 0,
16, 0, 0, 0, 0, 0,
26, 16, 2, 0, 42, 128,
32, 0, 0, 0, 0, 0,
0, 0, 0, 0, 31, 0,
4, 3, 58, 0, 16, 0,
0, 0, 0, 0, 62, 0,
0, 1, 21, 0, 0, 1,
41, 0, 0, 6, 130, 0,
16, 0, 0, 0, 0, 0,
10, 16, 2, 0, 1, 64,
0, 0, 8, 0, 0, 0,
30, 0, 0, 8, 18, 0,
16, 0, 1, 0, 0, 0,
58, 128, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 64, 0, 0, 255, 255,
255, 255, 54, 0, 0, 4,
18, 0, 16, 0, 2, 0,
0, 0, 26, 16, 2, 0,
54, 0, 0, 8, 194, 0,
16, 0, 2, 0, 0, 0,
2, 64, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 54, 0, 0, 4,
34, 0, 16, 0, 1, 0,
0, 0, 10, 32, 2, 0,
48, 0, 0, 1, 33, 0,
0, 7, 66, 0, 16, 0,
1, 0, 0, 0, 26, 0,
16, 0, 1, 0, 0, 0,
42, 0, 16, 0, 0, 0,
0, 0, 3, 0, 4, 3,
42, 0, 16, 0, 1, 0,
0, 0, 30, 0, 0, 7,
66, 0, 16, 0, 1, 0,
0, 0, 58, 0, 16, 0,
0, 0, 0, 0, 26, 0,
16, 0, 1, 0, 0, 0,
30, 0, 0, 8, 66, 0,
16, 0, 1, 0, 0, 0,
10, 0, 16, 128, 65, 0,
0, 0, 0, 0, 0, 0,
42, 0, 16, 0, 1, 0,
0, 0, 36, 0, 0, 7,
66, 0, 16, 0, 1, 0,
0, 0, 42, 0, 16, 0,
1, 0, 0, 0, 1, 64,
0, 0, 0, 0, 0, 0,
37, 0, 0, 7, 34, 0,
16, 0, 2, 0, 0, 0,
10, 0, 16, 0, 1, 0,
0, 0, 42, 0, 16, 0,
1, 0, 0, 0, 45, 0,
0, 137, 194, 0, 0, 128,
67, 85, 21, 0, 242, 0,
16, 0, 3, 0, 0, 0,
70, 14, 16, 0, 2, 0,
0, 0, 70, 126, 16, 0,
0, 0, 0, 0, 168, 0,
0, 9, 242, 240, 17, 0,
0, 0, 0, 0, 26, 0,
16, 0, 1, 0, 0, 0,
1, 64, 0, 0, 0, 0,
0, 0, 70, 14, 16, 0,
3, 0, 0, 0, 30, 0,
0, 7, 34, 0, 16, 0,
1, 0, 0, 0, 26, 0,
16, 0, 1, 0, 0, 0,
1, 64, 0, 0, 0, 1,
0, 0, 22, 0, 0, 1,
190, 24, 0, 1, 35, 0,
0, 10, 226, 0, 16, 0,
1, 0, 0, 0, 6, 16,
2, 0, 2, 64, 0, 0,
0, 0, 0, 0, 0, 1,
0, 0, 0, 1, 0, 0,
0, 1, 0, 0, 6, 32,
2, 0, 80, 0, 0, 8,
66, 0, 16, 0, 0, 0,
0, 0, 58, 0, 16, 0,
1, 0, 0, 0, 58, 128,
32, 0, 0, 0, 0, 0,
0, 0, 0, 0, 31, 0,
4, 3, 42, 0, 16, 0,
0, 0, 0, 0, 62, 0,
0, 1, 21, 0, 0, 1,
54, 0, 0, 8, 242, 0,
16, 0, 2, 0, 0, 0,
2, 64, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 54, 0, 0, 5,
66, 0, 16, 0, 0, 0,
0, 0, 1, 64, 0, 0,
0, 0, 0, 0, 48, 0,
0, 1, 34, 0, 0, 7,
130, 0, 16, 0, 0, 0,
0, 0, 26, 0, 16, 0,
0, 0, 0, 0, 42, 0,
16, 0, 0, 0, 0, 0,
3, 0, 4, 3, 58, 0,
16, 0, 0, 0, 0, 0,
30, 0, 0, 6, 130, 0,
16, 0, 0, 0, 0, 0,
42, 0, 16, 0, 0, 0,
0, 0, 10, 32, 2, 0,
167, 0, 0, 9, 242, 0,
16, 0, 3, 0, 0, 0,
58, 0, 16, 0, 0, 0,
0, 0, 1, 64, 0, 0,
0, 0, 0, 0, 70, 254,
17, 0, 0, 0, 0, 0,
0, 0, 0, 7, 242, 0,
16, 0, 2, 0, 0, 0,
70, 14, 16, 0, 2, 0,
0, 0, 70, 14, 16, 0,
3, 0, 0, 0, 30, 0,
0, 7, 66, 0, 16, 0,
0, 0, 0, 0, 42, 0,
16, 0, 0, 0, 0, 0,
1, 64, 0, 0, 1, 0,
0, 0, 22, 0, 0, 1,
140, 0, 0, 11, 18, 0,
16, 0, 0, 0, 0, 0,
1, 64, 0, 0, 31, 0,
0, 0, 1, 64, 0, 0,
1, 0, 0, 0, 10, 0,
16, 0, 0, 0, 0, 0,
1, 64, 0, 0, 1, 0,
0, 0, 43, 0, 0, 5,
18, 0, 16, 0, 0, 0,
0, 0, 10, 0, 16, 0,
0, 0, 0, 0, 14, 0,
0, 7, 242, 0, 16, 0,
0, 0, 0, 0, 70, 14,
16, 0, 2, 0, 0, 0,
6, 0, 16, 0, 0, 0,
0, 0, 54, 0, 0, 4,
18, 0, 16, 0, 1, 0,
0, 0, 26, 16, 2, 0,
164, 0, 0, 7, 242, 224,
17, 0, 0, 0, 0, 0,
70, 14, 16, 0, 1, 0,
0, 0, 70, 14, 16, 0,
0, 0, 0, 0, 21, 0,
0, 1, 62, 0, 0, 1,
83, 84, 65, 84, 148, 0,
0, 0, 89, 0, 0, 0,
4, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
4, 0, 0, 0, 27, 0,
0, 0, 4, 0, 0, 0,
7, 0, 0, 0, 8, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
12, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 0, 0,
0, 0, 2, 0, 0, 0
};

View File

@@ -1,115 +0,0 @@
//==============================================================================
//
// BoxBlurCS.hlsl
//
// D3D11 compute shader for separable box blur. Each dispatch performs
// either a horizontal or a vertical pass controlled by the Direction
// constant. Run four dispatches (H→V→H→V) to approximate a Gaussian.
//
// Uses group-shared memory so each texel is loaded once per thread
// group, then reused across the sliding window.
//
// Entry point:
// CSMain (cs_5_0)
//
// Recompile with:
// fxc /T cs_5_0 /E CSMain /Fh BoxBlurCS.h /Vn g_BoxBlurCS BoxBlurCS.hlsl
//
// Copyright (C) Mark Russinovich
// Sysinternals - www.sysinternals.com
//
//==============================================================================
// Input texture (read-only).
Texture2D<float4> InputTex : register(t0);
// Output texture (write-only).
RWTexture2D<float4> OutputTex : register(u0);
cbuffer BlurConstants : register(b0)
{
uint Direction; // 0 = horizontal, 1 = vertical
int Radius; // Box blur radius in pixels
uint Width; // Image width
uint Height; // Image height
};
// Thread group: 256 threads along the blur axis.
#define GROUP_SIZE 256
// Max radius we support. Shared memory = (GROUP_SIZE + 2*MAX_RADIUS) * 4 floats * 4 bytes
// = (256 + 64) * 16 = ~5 KB, well within the 32 KB limit.
#define MAX_RADIUS 32
// Shared memory tile: enough for the group + apron on both sides.
groupshared float4 tile[GROUP_SIZE + 2 * MAX_RADIUS];
[numthreads(GROUP_SIZE, 1, 1)]
void CSMain( uint3 groupId : SV_GroupID,
uint3 groupTid : SV_GroupThreadID,
uint3 dispatchId : SV_DispatchThreadID )
{
int r = min( Radius, MAX_RADIUS );
int tileSize = GROUP_SIZE + 2 * r;
int tid = (int)groupTid.x;
if( Direction == 0 )
{
// ── Horizontal pass ────────────────────────────────────────
int row = (int)groupId.y;
if( (uint)row >= Height )
return;
int groupStart = (int)groupId.x * GROUP_SIZE;
// Load tile: each thread loads its primary texel + apron.
for( int i = tid; i < tileSize; i += GROUP_SIZE )
{
int srcX = clamp( groupStart + i - r, 0, (int)Width - 1 );
tile[i] = InputTex[int2( srcX, row )];
}
GroupMemoryBarrierWithGroupSync();
int outX = groupStart + tid;
if( (uint)outX >= Width )
return;
// Sum the window from shared memory.
float4 sum = (float4)0;
int windowStart = tid; // tile index = tid + r - r
for( int k = 0; k <= 2 * r; k++ )
{
sum += tile[windowStart + k];
}
OutputTex[int2( outX, row )] = sum / (float)( 2 * r + 1 );
}
else
{
// ── Vertical pass ──────────────────────────────────────────
int col = (int)groupId.y;
if( (uint)col >= Width )
return;
int groupStart = (int)groupId.x * GROUP_SIZE;
// Load tile.
for( int i = tid; i < tileSize; i += GROUP_SIZE )
{
int srcY = clamp( groupStart + i - r, 0, (int)Height - 1 );
tile[i] = InputTex[int2( col, srcY )];
}
GroupMemoryBarrierWithGroupSync();
int outY = groupStart + tid;
if( (uint)outY >= Height )
return;
float4 sum = (float4)0;
int windowStart = tid;
for( int k = 0; k <= 2 * r; k++ )
{
sum += tile[windowStart + k];
}
OutputTex[int2( col, outY )] = sum / (float)( 2 * r + 1 );
}
}

View File

@@ -77,7 +77,6 @@ std::optional<CaptureFrame> CaptureFrameWait::TryGetNextFrame()
if (m_currentFrame != nullptr)
{
m_currentFrame.Close();
m_currentFrame = nullptr; // Prevent double-Close on subsequent calls
}
m_nextFrameEvent.ResetEvent();
@@ -108,33 +107,6 @@ std::optional<CaptureFrame> CaptureFrameWait::TryGetNextFrame()
}
//----------------------------------------------------------------------------
//
// CaptureFrameWait::PeekCurrentFrame
//
// Returns the frame that is currently held (if any) without closing
// it and without waiting for a new one. This is useful during
// recording startup: the constructor captured a frame when the
// session began, and OnMediaStreamSourceStarting can use it
// immediately instead of blocking until the next desktop change.
// The frame remains alive in the pool until the next
// TryGetNextFrame() call closes it.
//
//----------------------------------------------------------------------------
std::optional<CaptureFrame> CaptureFrameWait::PeekCurrentFrame() const
{
if (m_currentFrame != nullptr)
{
return std::optional<CaptureFrame>(
{
m_currentFrame.Surface(),
m_currentFrame.ContentSize(),
m_currentFrame.SystemRelativeTime(),
});
}
return std::nullopt;
}
//----------------------------------------------------------------------------
//
// CaptureFrameWait::TryGetNextFrame (with timeout)
@@ -149,7 +121,6 @@ std::optional<CaptureFrame> CaptureFrameWait::TryGetNextFrame( DWORD timeoutMs )
if( m_currentFrame != nullptr )
{
m_currentFrame.Close();
m_currentFrame = nullptr; // Prevent double-Close on subsequent calls
}
m_nextFrameEvent.ResetEvent();

View File

@@ -108,7 +108,6 @@ public:
std::optional<CaptureFrame> TryGetNextFrame();
std::optional<CaptureFrame> TryGetNextFrame( DWORD timeoutMs );
std::optional<CaptureFrame> PeekCurrentFrame() const;
void StopCapture();
void EnableCursorCapture( bool enable = true )
{

View File

@@ -1,113 +0,0 @@
#include "pch.h"
#include "NoiseSuppressor.h"
extern "C" {
#include "rnnoise/rnnoise.h"
}
// RNNoise processes 480 mono samples per frame (10ms at 48kHz)
static constexpr uint32_t RNNOISE_FRAME_SIZE = 480;
// RNNoise expects samples in PCM16 range (-32768 to 32767), not normalized float [-1, 1]
static constexpr float PCM16_SCALE = 32768.0f;
static constexpr float PCM16_SCALE_INV = 1.0f / 32768.0f;
NoiseSuppressor::NoiseSuppressor()
{
}
NoiseSuppressor::~NoiseSuppressor()
{
for (auto& channel : m_channels)
{
if (channel.state)
{
rnnoise_destroy(channel.state);
}
}
}
void NoiseSuppressor::EnsureChannelCount(uint32_t channels)
{
if (m_channels.size() == channels)
{
return;
}
// Channel count changed (or first call): rebuild per-channel RNNoise state.
for (auto& channel : m_channels)
{
if (channel.state)
{
rnnoise_destroy(channel.state);
}
}
m_channels.clear();
m_channels.resize(channels);
for (auto& channel : m_channels)
{
channel.state = rnnoise_create(nullptr);
}
}
void NoiseSuppressor::Process(float* samples, uint32_t sampleCount, uint32_t channels)
{
if (sampleCount == 0 || channels == 0)
{
return;
}
EnsureChannelCount(channels);
uint32_t frameCount = sampleCount / channels;
// Denoise each channel independently so the original channel layout is
// preserved (e.g. a mic wired only to the left channel stays on the left
// and silent channels stay silent instead of being filled with the voice).
for (uint32_t ch = 0; ch < channels; ch++)
{
ChannelState& channel = m_channels[ch];
if (!channel.state)
{
continue;
}
uint32_t residualCount = static_cast<uint32_t>(channel.residual.size());
uint32_t totalSamples = residualCount + frameCount;
channel.work.resize(totalSamples);
// Copy residual from previous call
if (residualCount > 0)
{
memcpy(channel.work.data(), channel.residual.data(), residualCount * sizeof(float));
}
// Deinterleave this channel and scale to PCM16 range for RNNoise
for (uint32_t i = 0; i < frameCount; i++)
{
channel.work[residualCount + i] = samples[i * channels + ch] * PCM16_SCALE;
}
// Process complete 480-sample frames through RNNoise
uint32_t processedSamples = 0;
while (processedSamples + RNNOISE_FRAME_SIZE <= totalSamples)
{
rnnoise_process_frame(channel.state, &channel.work[processedSamples], &channel.work[processedSamples]);
processedSamples += RNNOISE_FRAME_SIZE;
}
// Save unprocessed residual for next call
channel.residual.assign(
channel.work.begin() + processedSamples,
channel.work.end());
// Write denoised samples back to the interleaved buffer, scaling back to
// normalized float. Only this call's input region is written back.
for (uint32_t i = 0; i < frameCount; i++)
{
samples[i * channels + ch] = channel.work[residualCount + i] * PCM16_SCALE_INV;
}
}
}

View File

@@ -1,37 +0,0 @@
#pragma once
#include <vector>
#include <stdint.h>
struct DenoiseState;
class NoiseSuppressor
{
public:
NoiseSuppressor();
~NoiseSuppressor();
NoiseSuppressor(const NoiseSuppressor&) = delete;
NoiseSuppressor& operator=(const NoiseSuppressor&) = delete;
// Process interleaved multi-channel float samples in-place.
// Each channel is denoised independently through its own RNNoise state in
// 480-sample frames, preserving the original channel layout (e.g. a mic
// wired only to the left channel stays on the left and is not duplicated
// onto the right).
void Process(float* samples, uint32_t sampleCount, uint32_t channels);
private:
// Per-channel RNNoise state and buffers so each channel is denoised
// independently and the channel layout is preserved.
struct ChannelState
{
DenoiseState* state = nullptr;
std::vector<float> work; // Working buffer for the current quantum
std::vector<float> residual; // Leftover samples from previous quantum
};
void EnsureChannelCount(uint32_t channels);
std::vector<ChannelState> m_channels;
};

View File

@@ -32,9 +32,6 @@ extern DWORD g_WebcamPosition;
extern DWORD g_WebcamSize;
extern DWORD g_WebcamShape;
extern TCHAR g_WebcamDeviceSymLink[MAX_PATH];
extern DWORD g_WebcamBackgroundMode;
extern TCHAR g_WebcamBackgroundImage[];
extern DWORD g_WebcamBrightness;
extern class ClassRegistry reg;
extern REG_SETTING RegSettings[];
extern HINSTANCE g_hInstance;
@@ -109,34 +106,6 @@ static double RecDiagElapsedMs()
return static_cast<double>( now.QuadPart - s_origin.QuadPart ) * 1000.0 / s_freq.QuadPart;
}
static FILE* s_recDiagFile = nullptr;
static void RecDiagOpenFile()
{
if( !s_recDiagFile )
{
wchar_t path[MAX_PATH];
if( ExpandEnvironmentStringsW( L"%TEMP%\\ZoomIt_RecDiag.log", path, MAX_PATH ) )
{
_wfopen_s( &s_recDiagFile, path, L"a" );
if( s_recDiagFile )
{
fwprintf( s_recDiagFile, L"\n===== NEW SESSION =====\n" );
fflush( s_recDiagFile );
}
}
}
}
static void RecDiagCloseFile()
{
if( s_recDiagFile )
{
fclose( s_recDiagFile );
s_recDiagFile = nullptr;
}
}
static void RecDiag( const wchar_t* fmt, ... )
{
wchar_t buf[512];
@@ -154,19 +123,8 @@ static void RecDiag( const wchar_t* fmt, ... )
_vsnwprintf_s( buf + offset, _countof( buf ) - offset, _TRUNCATE, fmt, va );
va_end( va );
OutputDebugStringW( buf );
RecDiagOpenFile();
if( s_recDiagFile )
{
fwprintf( s_recDiagFile, L"%s", buf );
fflush( s_recDiagFile );
}
}
static int s_diagVideoCount = 0;
static int s_diagAudioCount = 0;
static int64_t s_diagStartTs = 0; // SystemRelativeTime from OnStarting
static bool IsGifPath(const std::wstring& path)
{
try
@@ -972,20 +930,12 @@ VideoRecordingSession::VideoRecordingSession(
winrt::GraphicsCaptureItem const& item,
RECT const cropRect,
uint32_t frameRate,
std::unique_ptr<AudioSampleGenerator> audioGenerator,
winrt::IAsyncAction audioInitAction,
bool captureAudio,
bool captureSystemAudio,
bool micMonoMix,
winrt::Streams::IRandomAccessStream const& stream)
{
RecDiag( L"Constructor: entry\n" );
// Take ownership of pre-created audio generator. Its InitializeAsync
// was started in StartRecordingAsync so it runs in parallel with all
// the D3D, capture-item, and webcam setup below.
m_audioGenerator = std::move( audioGenerator );
m_audioInitAction = audioInitAction;
RecDiag( L"Constructor: audio generator received (init %s)\n",
m_audioInitAction ? L"pending" : L"none" );
m_device = device;
m_d3dDevice = GetDXGIInterfaceFromObject<ID3D11Device>(m_device);
m_d3dDevice->GetImmediateContext(m_d3dContext.put());
@@ -1088,15 +1038,8 @@ VideoRecordingSession::VideoRecordingSession(
probeCapture.Close();
return true;
}
catch( winrt::hresult_error const& ex )
{
RecDiag( L"Constructor: webcam probe failed hr=0x%08X: %s\n",
static_cast<unsigned>( ex.code() ), ex.message().c_str() );
return false;
}
catch( ... )
{
RecDiag( L"Constructor: webcam probe failed with unknown exception\n" );
return false;
}
});
@@ -1124,10 +1067,7 @@ VideoRecordingSession::VideoRecordingSession(
static_cast<WebcamCapture::Position>( g_WebcamPosition ),
static_cast<WebcamCapture::Size>( g_WebcamSize ),
webcamShape,
isFullScreenRecording,
static_cast<WebcamBackgroundMode>( g_WebcamBackgroundMode ),
g_WebcamBackgroundImage,
static_cast<int>( g_WebcamBrightness ) );
isFullScreenRecording );
m_webcamCapture->Start();
RecDiag( L"Constructor: WebcamCapture::Start() returned\n" );
}
@@ -1149,13 +1089,10 @@ VideoRecordingSession::VideoRecordingSession(
// Store frame interval for timeout-based frame production when webcam is active.
m_frameIntervalTicks = ( frameRate > 0 ) ? ( 10'000'000LL / frameRate ) : 333'333LL;
if (m_audioGenerator)
{
// Always set up audio profile for loopback capture (stereo AAC)
auto audioProps = m_audioGenerator->GetEncodingProperties();
m_encodingProfile.Audio(winrt::AudioEncodingProperties::CreateAac(
audioProps.SampleRate(), audioProps.ChannelCount(), 192000));
}
// NOTE: Audio encoding profile (m_encodingProfile.Audio) is set in
// StartAsync() after the audio graph is fully initialized, not here.
// Calling GetEncodingProperties() before InitializeAsync completes
// would crash because m_audioOutputNode is still null.
// Describe our input: uncompressed BGRA8 buffers
auto properties = winrt::VideoEncodingProperties::CreateUncompressed(
@@ -1173,6 +1110,15 @@ VideoRecordingSession::VideoRecordingSession(
DXGI_FORMAT_B8G8R8A8_UNORM,
2);
if (captureAudio || captureSystemAudio)
{
m_audioGenerator = std::make_unique<AudioSampleGenerator>(captureAudio, captureSystemAudio, micMonoMix);
}
else
{
m_audioGenerator = nullptr;
}
// Wait for the webcam's first frame now that all other setup is done.
// The camera was started early, so most of its ~850 ms sensor warmup has
// overlapped with the encoding profile, swap chain, and audio generator
@@ -1206,7 +1152,6 @@ VideoRecordingSession::VideoRecordingSession(
VideoRecordingSession::~VideoRecordingSession()
{
Close();
RecDiagCloseFile();
}
@@ -1225,15 +1170,19 @@ winrt::IAsyncAction VideoRecordingSession::StartAsync()
// Create our MediaStreamSource
if(m_audioGenerator) {
RecDiag( L"StartAsync: co_await audio init...\n" );
if (m_audioInitAction) {
co_await m_audioInitAction; // started in constructor
m_audioInitAction = nullptr;
} else {
co_await m_audioGenerator->InitializeAsync();
}
RecDiag( L"StartAsync: co_await InitializeAsync...\n" );
co_await m_audioGenerator->InitializeAsync();
RecDiag( L"StartAsync: audio initialized\n" );
m_streamSource = winrt::MediaStreamSource(m_videoDescriptor, winrt::AudioStreamDescriptor(m_audioGenerator->GetEncodingProperties()));
// Set up the audio encoding profile now that the audio graph is
// fully initialized. GetEncodingProperties() requires
// m_audioOutputNode to be valid, which is only guaranteed after
// InitializeAsync completes.
auto audioProps = m_audioGenerator->GetEncodingProperties();
m_encodingProfile.Audio(winrt::AudioEncodingProperties::CreateAac(
audioProps.SampleRate(), audioProps.ChannelCount(), 192000));
m_streamSource = winrt::MediaStreamSource(m_videoDescriptor, winrt::AudioStreamDescriptor(audioProps));
}
else {
@@ -1293,9 +1242,6 @@ winrt::IAsyncAction VideoRecordingSession::StartAsync()
//----------------------------------------------------------------------------
void VideoRecordingSession::Close()
{
RecDiag( L"Close: totalVideoFrames=%d totalAudioSamples=%d\n",
s_diagVideoCount, s_diagAudioCount );
// Stop webcam capture before closing the main session.
if( m_webcamCapture )
{
@@ -1345,45 +1291,28 @@ void VideoRecordingSession::OnMediaStreamSourceStarting(
winrt::MediaStreamSource const&,
winrt::MediaStreamSourceStartingEventArgs const& args)
{
// Close the stale frame captured in the constructor (~1-2 seconds
// ago) and grab a FRESH frame via TryGetNextFrame. This gives us
// both current visual content and a current SystemRelativeTime,
// avoiding the frozen-first-frame artefact. WGC delivers a new
// frame within one vblank (~16 ms) after the old frame is released
// back to the pool, so a 200 ms timeout is very generous.
RecDiag( L"OnStarting: calling TryGetNextFrame(200) for fresh frame...\n" );
auto frame = m_frameWait->TryGetNextFrame( 200 );
int64_t startSRT = 0;
RecDiag( L"OnStarting: entry, calling TryGetNextFrame...\n" );
auto frame = m_frameWait->TryGetNextFrame();
if (frame) {
startSRT = frame->SystemRelativeTime.count();
RecDiag( L"OnStarting: got fresh frame, SRT=%lld (%.1fms)\n",
startSRT, startSRT / 10000.0 );
RecDiag( L"OnStarting: got frame, SystemRelativeTime=%lld (%.1fms)\n",
frame->SystemRelativeTime.count(),
frame->SystemRelativeTime.count() / 10000.0 );
args.Request().SetActualStartPosition(frame->SystemRelativeTime);
args.Request().SetActualStartPosition( frame->SystemRelativeTime );
// Cache this frame so it can be served as the very first video
// sample in OnMediaStreamSourceSampleRequested. Without this,
// the frame is discarded and the first encoded sample comes from
// the *next* capture, creating a visible timestamp gap.
m_cachedStartingFrame = frame;
m_adjustedStartSRT = startSRT;
} else {
// Timeout (very unlikely). Fall back to QPC-derived SRT.
// Use double for the intermediate product to avoid int64
// overflow (naive integer multiply overflows after ~25.6 h).
RecDiag( L"OnStarting: TryGetNextFrame timed out, using QPC fallback\n" );
LARGE_INTEGER qpcFreq, qpcNow;
QueryPerformanceFrequency( &qpcFreq );
QueryPerformanceCounter( &qpcNow );
startSRT = static_cast<int64_t>(
static_cast<double>( qpcNow.QuadPart ) * 10'000'000.0 / qpcFreq.QuadPart );
args.Request().SetActualStartPosition( winrt::TimeSpan{ startSRT } );
m_adjustedStartSRT = startSRT;
}
// Start audio capture. Pass the video start SRT so audio
// timestamps can be rebased to the same domain as video.
if (m_audioGenerator) {
RecDiag( L"OnStarting: calling AudioSampleGenerator::Start(videoStartSRT=%lld)\n", startSRT );
m_audioGenerator->Start( startSRT );
RecDiag( L"OnStarting: audio started\n" );
if (m_audioGenerator) {
RecDiag( L"OnStarting: calling AudioSampleGenerator::Start()\n" );
m_audioGenerator->Start();
RecDiag( L"OnStarting: audio started\n" );
}
} else {
RecDiag( L"OnStarting: TryGetNextFrame returned nullopt!\n" );
}
RecDiag( L"OnStarting: exit\n" );
}
@@ -1398,11 +1327,12 @@ std::shared_ptr<VideoRecordingSession> VideoRecordingSession::Create(
winrt::GraphicsCaptureItem const& item,
RECT const& crop,
uint32_t frameRate,
std::unique_ptr<AudioSampleGenerator> audioGenerator,
winrt::IAsyncAction audioInitAction,
bool captureAudio,
bool captureSystemAudio,
bool micMonoMix,
winrt::Streams::IRandomAccessStream const& stream)
{
return std::shared_ptr<VideoRecordingSession>(new VideoRecordingSession(device, item, crop, frameRate, std::move(audioGenerator), audioInitAction, stream));
return std::shared_ptr<VideoRecordingSession>(new VideoRecordingSession(device, item, crop, frameRate, captureAudio, captureSystemAudio, micMonoMix, stream));
}
//----------------------------------------------------------------------------
@@ -1414,6 +1344,10 @@ void VideoRecordingSession::OnMediaStreamSourceSampleRequested(
winrt::MediaStreamSource const&,
winrt::MediaStreamSourceSampleRequestedEventArgs const& args)
{
static int s_diagVideoCount = 0;
static int s_diagAudioCount = 0;
static int64_t s_diagStartTs = 0; // SystemRelativeTime from OnStarting
auto request = args.Request();
auto streamDescriptor = request.StreamDescriptor();
if (auto videoStreamDescriptor = streamDescriptor.try_as<winrt::VideoStreamDescriptor>())
@@ -1484,16 +1418,7 @@ void VideoRecordingSession::OnMediaStreamSourceSampleRequested(
else
{
// New desktop frame — crop and copy to back buffer.
// If this is the cached starting frame, use the adjusted
// SRT (computed from QPC in OnStarting) instead of the
// stale SRT from the constructor. The stale SRT is ~2-3s
// behind, creating a massive timestamp gap between frame
// #1 and #2 that causes the transcoder to starve video
// while filling the gap with audio.
if( cachedFrame.has_value() && m_adjustedStartSRT != 0 )
timeStamp = winrt::TimeSpan{ m_adjustedStartSRT };
else
timeStamp = frame->SystemRelativeTime;
timeStamp = frame->SystemRelativeTime;
auto contentSize = frame->ContentSize;
auto frameTexture = GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame->FrameTexture);
D3D11_TEXTURE2D_DESC desc = {};
@@ -1529,7 +1454,7 @@ void VideoRecordingSession::OnMediaStreamSourceSampleRequested(
m_d3dContext->CopyResource( m_cachedDesktopTex.get(), backBuffer.get() );
}
// Log first 50 video frames with timing.
// Log first 10 video frames with timing.
if( !m_hasVideoSample.load() )
{
s_diagVideoCount = 0;
@@ -1543,14 +1468,13 @@ void VideoRecordingSession::OnMediaStreamSourceSampleRequested(
m_hasQpcOrigin = true;
}
s_diagVideoCount++;
if( s_diagVideoCount <= 50 )
if( s_diagVideoCount <= 10 )
{
RecDiag( L"SampleReq VIDEO #%d: sysRelTime=%lld deltaFromStart=%.1fms repeat=%d cached=%d\n",
RecDiag( L"SampleReq VIDEO #%d: sysRelTime=%lld deltaFromStart=%.1fms repeat=%d\n",
s_diagVideoCount,
timeStamp.count(),
( timeStamp.count() - s_diagStartTs ) / 10000.0,
isRepeatFrame ? 1 : 0,
( s_diagVideoCount == 1 && cachedFrame.has_value() ) ? 1 : 0 );
isRepeatFrame ? 1 : 0 );
}
#if _DEBUG
@@ -1574,11 +1498,6 @@ void VideoRecordingSession::OnMediaStreamSourceSampleRequested(
// Composite webcam overlay onto the back buffer.
if( m_webcamCapture )
{
if( s_diagVideoCount <= 3 )
{
RecDiag( L"SampleReq VIDEO #%d: compositing LIVE webcam frame\n",
s_diagVideoCount );
}
m_webcamCapture->CompositeOnto( backBuffer.get() );
}
@@ -1628,9 +1547,7 @@ void VideoRecordingSession::OnMediaStreamSourceSampleRequested(
}
catch (winrt::hresult_error const& error)
{
RecDiag( L"SampleReq VIDEO EXCEPTION on frame #%d: hr=0x%08X %s\n",
s_diagVideoCount, static_cast<unsigned>(error.code()),
error.message().c_str() );
OutputDebugStringW(error.message().c_str());
request.Sample(nullptr);
CloseInternal();
return;
@@ -1640,53 +1557,26 @@ void VideoRecordingSession::OnMediaStreamSourceSampleRequested(
{
try
{
static int s_audioReqCount = 0;
if( !m_hasVideoSample.load() )
s_audioReqCount = 0;
s_audioReqCount++;
if( s_audioReqCount <= 5 )
{
RecDiag( L"SampleReq AUDIO req #%d: calling TryGetNextSample (started=%d)...\n",
s_audioReqCount, m_audioGenerator ? 1 : 0 );
}
LARGE_INTEGER tBefore, tAfter, tFreq;
QueryPerformanceFrequency( &tFreq );
QueryPerformanceCounter( &tBefore );
if (auto sample = m_audioGenerator ? m_audioGenerator->TryGetNextSample() : std::optional<winrt::MediaStreamSample>{}; sample.has_value())
{
QueryPerformanceCounter( &tAfter );
double waitMs = static_cast<double>( tAfter.QuadPart - tBefore.QuadPart ) * 1000.0 / tFreq.QuadPart;
s_diagAudioCount++;
if( s_diagAudioCount <= 50 )
if( s_diagAudioCount <= 10 )
{
auto ts = sample.value().Timestamp().count();
auto dur = sample.value().Duration().count();
RecDiag( L"SampleReq AUDIO #%d (req %d): timestamp=%lld (%.1fms) duration=%lld (%.1fms) waitMs=%.1f\n",
s_diagAudioCount, s_audioReqCount,
ts, ts / 10000.0,
dur, dur / 10000.0,
waitMs );
RecDiag( L"SampleReq AUDIO #%d: timestamp=%lld (%.1fms)\n",
s_diagAudioCount,
sample.value().Timestamp().count(),
sample.value().Timestamp().count() / 10000.0 );
}
request.Sample(sample.value());
}
else
{
QueryPerformanceCounter( &tAfter );
double waitMs = static_cast<double>( tAfter.QuadPart - tBefore.QuadPart ) * 1000.0 / tFreq.QuadPart;
RecDiag( L"SampleReq AUDIO req #%d: TryGetNextSample returned EMPTY after %.1fms → end-of-audio-stream\n",
s_audioReqCount, waitMs );
request.Sample(nullptr);
}
}
catch (winrt::hresult_error const& error)
{
RecDiag( L"SampleReq AUDIO EXCEPTION on sample #%d: hr=0x%08X %s\n",
s_diagAudioCount, static_cast<unsigned>(error.code()),
error.message().c_str() );
OutputDebugStringW(error.message().c_str());
request.Sample(nullptr);
CloseInternal();
return;
@@ -1775,7 +1665,7 @@ public:
}
return S_OK;
}
IFACEMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) { return S_OK; }
IFACEMETHODIMP OnSelectionChange(IFileDialog*) { return S_OK; }
IFACEMETHODIMP OnShareViolation(IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) { return S_OK; }
@@ -2954,7 +2844,7 @@ static void HandlePlaybackCommand(int controlId, VideoRecordingSession::TrimDial
case IDC_TRIM_REWIND:
{
StopPlayback(hDlg, pData, false);
// Use 1 second step for timelines < 20 seconds, 2 seconds
// Use 1 second step for timelines < 20 seconds, 2 seconds
const int64_t duration = pData->trimEnd.count() - pData->trimStart.count();
const int64_t stepTicks = (duration < 200'000'000) ? 10'000'000 : kJogStepTicks;
const int64_t newTicks = (std::max)(pData->trimStart.count(), pData->currentPosition.count() - stepTicks);
@@ -2970,7 +2860,7 @@ static void HandlePlaybackCommand(int controlId, VideoRecordingSession::TrimDial
case IDC_TRIM_FORWARD:
{
StopPlayback(hDlg, pData, false);
// Use 1 second step for timelines < 20 seconds, 2 seconds
// Use 1 second step for timelines < 20 seconds, 2 seconds
const int64_t duration = pData->trimEnd.count() - pData->trimStart.count();
const int64_t stepTicks = (duration < 200'000'000) ? 10'000'000 : kJogStepTicks;
const int64_t newTicks = (std::min)(pData->trimEnd.count(), pData->currentPosition.count() + stepTicks);
@@ -5633,7 +5523,7 @@ INT_PTR CALLBACK VideoRecordingSession::TrimDialogProc(HWND hDlg, UINT message,
const auto relativePos = winrt::TimeSpan{ (std::max)(pData->currentPosition.count() - pData->trimStart.count(), int64_t{ 0 }) };
SetTimeText(hDlg, IDC_TRIM_POSITION_LABEL, relativePos, true);
}
if (elapsedMs >= frameDurationMs)
{
// Time to advance to next frame

View File

@@ -27,8 +27,9 @@ public:
winrt::GraphicsCaptureItem const& item,
RECT const& cropRect,
uint32_t frameRate,
std::unique_ptr<AudioSampleGenerator> audioGenerator,
winrt::Windows::Foundation::IAsyncAction audioInitAction,
bool captureAudio,
bool captureSystemAudio,
bool micMonoMix,
winrt::Streams::IRandomAccessStream const& stream);
~VideoRecordingSession();
@@ -216,8 +217,9 @@ private:
winrt::Capture::GraphicsCaptureItem const& item,
RECT const cropRect,
uint32_t frameRate,
std::unique_ptr<AudioSampleGenerator> audioGenerator,
winrt::Windows::Foundation::IAsyncAction audioInitAction,
bool captureAudio,
bool captureSystemAudio,
bool micMonoMix,
winrt::Streams::IRandomAccessStream const& stream);
void CloseInternal();
@@ -277,9 +279,5 @@ private:
LARGE_INTEGER m_qpcFreq{};
LARGE_INTEGER m_qpcRecordingStart{}; // QPC at first sample
int64_t m_startSystemRelativeTime = 0; // SystemRelativeTime of first frame
int64_t m_adjustedStartSRT = 0; // QPC-based current SRT set in OnStarting
bool m_hasQpcOrigin = false;
// Audio initialization started in the constructor, awaited in StartAsync.
winrt::Windows::Foundation::IAsyncAction m_audioInitAction{ nullptr };
};

File diff suppressed because it is too large Load Diff

View File

@@ -18,37 +18,11 @@
#include <mfreadwrite.h>
#include <atomic>
#include <condition_variable>
#include <memory>
#include "BackgroundBlur.h"
#include <mutex>
#include <thread>
#include <vector>
#include <winrt/base.h>
class BackgroundBlur;
// Must match CompositeConstants cbuffer layout in WebcamComposite.hlsl.
struct GpuCompositeConstants
{
float CropOffsetX, CropOffsetY; // Camera crop UV offset
float CropScaleX, CropScaleY; // Camera crop UV scale
float Gamma; // Gamma correction exponent
float CornerRadius; // Corner radius in output pixels
float OutputW, OutputH; // Output dimensions
UINT ShapeType; // 0=Square, 1=RoundedRect, 2=RoundedSquare, 3=Circle
UINT HasMask; // 1 if mask texture valid
float Pad[2];
};
// Must match BlurConstants cbuffer layout in BoxBlurCS.hlsl.
struct GpuBlurConstants
{
UINT Direction; // 0 = horizontal, 1 = vertical
INT Radius; // Box blur radius in pixels
UINT Width; // Image width
UINT Height; // Image height
};
class WebcamCapture
{
public:
@@ -70,10 +44,7 @@ public:
Position position,
Size size,
Shape shape,
bool fullScreenRecording = false,
WebcamBackgroundMode backgroundMode = WebcamBackgroundMode::None,
const wchar_t* backgroundImagePath = nullptr,
int brightness = 50 );
bool fullScreenRecording = false );
~WebcamCapture();
// Start/stop the capture thread.
@@ -135,19 +106,6 @@ private:
bool InitSourceReader();
RECT ComputeDestRect() const;
void ComputeOverlayDimensions();
bool InitGpuComposite();
bool GpuComposite( const UINT32* cameraPixels, UINT camW, UINT camH,
const UINT32* blurPixels, UINT blurW, UINT blurH,
const float* mask, UINT maskW, UINT maskH,
UINT outW, UINT outH,
UINT srcCropX, UINT srcCropY, UINT srcCropW, UINT srcCropH,
float gamma, Shape shape, float cornerRadius,
ID3D11ShaderResourceView* preBlurSRV = nullptr );
// GPU box blur: runs 4 compute-shader dispatches (H→V→H→V) on the
// processing-resolution frame. The result stays GPU-resident in
// m_blurPingPong[0] for direct use by GpuComposite.
bool GpuBoxBlur( const UINT32* pixels, UINT width, UINT height, int radius );
winrt::com_ptr<ID3D11Device> m_d3dDevice;
winrt::com_ptr<ID3D11DeviceContext> m_d3dContext;
@@ -177,7 +135,6 @@ private:
// Reusable frame buffer for the capture thread (avoids per-frame alloc).
std::vector<BYTE> m_framePixels;
std::vector<BYTE> m_scaledPixels;
std::vector<BYTE> m_upscalePixels;
UINT m_overlayW = 0;
UINT m_overlayH = 0;
@@ -185,11 +142,6 @@ private:
UINT m_camHeight = 0;
RECT m_destRect = {};
// Brightness correction (user-controlled, fixed gamma LUT).
int m_brightness = 50; // 0=dark, 50=neutral, 100=bright
std::array<uint8_t, 256> m_gammaLUT = {}; // current LUT
double m_lutGamma = 1.0; // gamma used for m_gammaLUT
// Output dimensions (recording output after crop+scale).
UINT m_outputWidth = 0;
UINT m_outputHeight = 0;
@@ -211,57 +163,8 @@ private:
std::condition_variable m_readyCV;
bool m_firstFrameCaptured = false;
// Background processing.
WebcamBackgroundMode m_backgroundMode = WebcamBackgroundMode::None;
std::wstring m_backgroundImagePath;
std::unique_ptr<BackgroundBlur> m_backgroundBlur;
// Debug counters for CompositeOnto logging.
int m_compositeCount = 0;
int m_lockFailCount = 0;
int m_uploadCount = 0;
// ── GPU composite pipeline ──────────────────────────────
// Separate D3D device for capture thread (avoids contention
// with the recording session's device/context).
winrt::com_ptr<ID3D11Device> m_gpuDevice;
winrt::com_ptr<ID3D11DeviceContext> m_gpuContext;
winrt::com_ptr<ID3D11VertexShader> m_compositeVS;
winrt::com_ptr<ID3D11PixelShader> m_compositePS;
winrt::com_ptr<ID3D11Buffer> m_compositeCB;
winrt::com_ptr<ID3D11SamplerState> m_bilinearSampler;
winrt::com_ptr<ID3D11RasterizerState> m_gpuRasterState;
winrt::com_ptr<ID3D11BlendState> m_gpuBlendState;
// Input textures + SRVs (recreated when dimensions change).
winrt::com_ptr<ID3D11Texture2D> m_gpuCameraTex;
winrt::com_ptr<ID3D11ShaderResourceView> m_gpuCameraSRV;
UINT m_gpuCameraW = 0, m_gpuCameraH = 0;
winrt::com_ptr<ID3D11Texture2D> m_gpuBlurTex;
winrt::com_ptr<ID3D11ShaderResourceView> m_gpuBlurSRV;
UINT m_gpuBlurW = 0, m_gpuBlurH = 0;
winrt::com_ptr<ID3D11Texture2D> m_gpuMaskTex;
winrt::com_ptr<ID3D11ShaderResourceView> m_gpuMaskSRV;
UINT m_gpuMaskW = 0, m_gpuMaskH = 0;
// Render target + staging for readback.
winrt::com_ptr<ID3D11Texture2D> m_gpuRenderTarget;
winrt::com_ptr<ID3D11RenderTargetView> m_gpuRTV;
winrt::com_ptr<ID3D11Texture2D> m_gpuStaging;
UINT m_gpuRTW = 0, m_gpuRTH = 0;
bool m_gpuCompositeReady = false;
// ── GPU box-blur compute pipeline ───────────────────────
winrt::com_ptr<ID3D11ComputeShader> m_blurCS;
winrt::com_ptr<ID3D11Buffer> m_blurCB;
// Ping-pong textures with SRV + UAV for blur passes.
winrt::com_ptr<ID3D11Texture2D> m_blurPingPong[2];
winrt::com_ptr<ID3D11ShaderResourceView> m_blurPingSRV[2];
winrt::com_ptr<ID3D11UnorderedAccessView> m_blurPingUAV[2];
UINT m_blurPPW = 0, m_blurPPH = 0;
bool m_gpuBlurReady = false;
};

View File

@@ -1,143 +0,0 @@
//==============================================================================
//
// WebcamComposite.hlsl
//
// GPU composite shader for webcam overlay.
// Composites sharp foreground from full-resolution camera with
// blurred background from processing-resolution blur buffer, using
// a segmentation mask to blend between the two sources.
//
// The GPU's hardware texture sampler provides free bilinear filtering,
// making this orders of magnitude faster than the equivalent CPU loop.
//
// Entry points:
// VSMain (vs_5_0) — full-screen triangle, no vertex buffer needed
// PSMain (ps_5_0) — composite camera + blur + mask + shape mask
//
// Recompile with:
// fxc /T vs_5_0 /E VSMain /Fh WebcamCompositeVS.h /Vn g_WebcamCompositeVS WebcamComposite.hlsl
// fxc /T ps_5_0 /E PSMain /Fh WebcamCompositePS.h /Vn g_WebcamCompositePS WebcamComposite.hlsl
//
// Copyright (C) Mark Russinovich
// Sysinternals - www.sysinternals.com
//
//==============================================================================
// Camera frame at full resolution (e.g. 1920x1080), B8G8R8A8_UNORM.
// Shader sees RGBA due to hardware swizzle.
Texture2D CameraTex : register(t0);
// Blurred processing buffer at reduced resolution (e.g. 960x540), B8G8R8A8_UNORM.
// Already gamma-corrected from CPU downsample.
Texture2D BlurTex : register(t1);
// Segmentation mask from ONNX model, R32_FLOAT.
// 1.0 = foreground (person), 0.0 = background.
Texture2D MaskTex : register(t2);
// Bilinear sampler with clamp addressing — used for all textures.
SamplerState BilinearSamp : register(s0);
cbuffer CompositeConstants : register(b0)
{
float2 CropOffset; // Camera crop UV offset (srcCropX/camW, srcCropY/camH)
float2 CropScale; // Camera crop UV scale (srcCropW/camW, srcCropH/camH)
float Gamma; // Gamma correction exponent (< 1 brightens)
float CornerRadius; // Corner radius in output pixels
float OutputW; // Output width in pixels
float OutputH; // Output height in pixels
uint ShapeType; // 0=Square, 1=RoundedRect, 2=RoundedSquare, 3=Circle
uint HasMask; // 1 if segmentation mask is valid
float2 Pad;
};
struct VSOutput
{
float4 Position : SV_POSITION;
float2 TexCoord : TEXCOORD0;
};
//----------------------------------------------------------------------------
// Vertex shader: full-screen triangle from SV_VertexID (no vertex buffer).
// Draw(3, 0) to invoke. The triangle covers [-1,1] clip space.
//----------------------------------------------------------------------------
VSOutput VSMain( uint vertexId : SV_VertexID )
{
VSOutput output;
output.TexCoord = float2( (vertexId << 1) & 2, vertexId & 2 );
output.Position = float4( output.TexCoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0 );
return output;
}
//----------------------------------------------------------------------------
// Pixel shader: composite camera foreground with blurred background.
// - Shape masking (circle, rounded rect) produces alpha = 0 outside.
// - Segmentation mask blends camera (foreground) with blur (background).
// - Gamma correction applied to camera samples only (blur already corrected).
// - Hardware bilinear filtering on all texture samples (free).
//----------------------------------------------------------------------------
float4 PSMain( VSOutput input ) : SV_TARGET
{
float2 uv = input.TexCoord;
float px = uv.x * OutputW;
float py = uv.y * OutputH;
// ── Shape mask ─────────────────────────────────────────────────────
if( ShapeType == 3 ) // Circle
{
float halfW = OutputW * 0.5;
float halfH = OutputH * 0.5;
float radius = min( halfW, halfH );
float dx = ( px - halfW ) / radius;
float dy = ( py - halfH ) / radius;
if( dx * dx + dy * dy > 1.0 )
return float4( 0, 0, 0, 0 );
}
else if( ShapeType >= 1 ) // RoundedRect or RoundedSquare
{
float cx = 0, cy = 0;
bool inCorner = false;
if( px < CornerRadius && py < CornerRadius )
{ cx = CornerRadius; cy = CornerRadius; inCorner = true; }
else if( px > OutputW - CornerRadius && py < CornerRadius )
{ cx = OutputW - CornerRadius; cy = CornerRadius; inCorner = true; }
else if( px < CornerRadius && py > OutputH - CornerRadius )
{ cx = CornerRadius; cy = OutputH - CornerRadius; inCorner = true; }
else if( px > OutputW - CornerRadius && py > OutputH - CornerRadius )
{ cx = OutputW - CornerRadius; cy = OutputH - CornerRadius; inCorner = true; }
if( inCorner )
{
float ddx = px - cx;
float ddy = py - cy;
if( ddx * ddx + ddy * ddy > CornerRadius * CornerRadius )
return float4( 0, 0, 0, 0 );
}
}
// ── Composite ──────────────────────────────────────────────────────
if( HasMask )
{
// Segmentation mask (bilinear-filtered for smooth edges).
float mask = saturate( MaskTex.Sample( BilinearSamp, uv ).r );
// Camera: crop-to-fill UV mapping + gamma correction.
float2 camUV = CropOffset + uv * CropScale;
float4 cam = CameraTex.Sample( BilinearSamp, camUV );
cam.rgb = pow( max( cam.rgb, 0.001 ), Gamma );
// Blur: already gamma-corrected from CPU downsample.
float4 blur = BlurTex.Sample( BilinearSamp, uv );
// Blend: mask=1 → camera (foreground), mask=0 → blur (background).
float3 result = lerp( blur.rgb, cam.rgb, mask );
return float4( result, 1.0 );
}
else
{
// No segmentation mask — just display the processing buffer.
float4 blur = BlurTex.Sample( BilinearSamp, uv );
return float4( blur.rgb, 1.0 );
}
}

View File

@@ -1,616 +0,0 @@
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 10.1
//
//
// Buffer Definitions:
//
// cbuffer CompositeConstants
// {
//
// float2 CropOffset; // Offset: 0 Size: 8
// float2 CropScale; // Offset: 8 Size: 8
// float Gamma; // Offset: 16 Size: 4
// float CornerRadius; // Offset: 20 Size: 4
// float OutputW; // Offset: 24 Size: 4
// float OutputH; // Offset: 28 Size: 4
// uint ShapeType; // Offset: 32 Size: 4
// uint HasMask; // Offset: 36 Size: 4
// float2 Pad; // Offset: 40 Size: 8 [unused]
//
// }
//
//
// Resource Bindings:
//
// Name Type Format Dim HLSL Bind Count
// ------------------------------ ---------- ------- ----------- -------------- ------
// BilinearSamp sampler NA NA s0 1
// CameraTex texture float4 2d t0 1
// BlurTex texture float4 2d t1 1
// MaskTex texture float4 2d t2 1
// CompositeConstants cbuffer NA NA cb0 1
//
//
//
// Input signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_POSITION 0 xyzw 0 POS float
// TEXCOORD 0 xy 1 NONE float xy
//
//
// Output signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_TARGET 0 xyzw 0 TARGET float xyzw
//
ps_5_0
dcl_globalFlags refactoringAllowed
dcl_constantbuffer CB0[3], immediateIndexed
dcl_sampler s0, mode_default
dcl_resource_texture2d (float,float,float,float) t0
dcl_resource_texture2d (float,float,float,float) t1
dcl_resource_texture2d (float,float,float,float) t2
dcl_input_ps linear v1.xy
dcl_output o0.xyzw
dcl_temps 4
mul r0.xy, v1.xyxx, cb0[1].zwzz
ieq r0.z, cb0[2].x, l(3)
if_nz r0.z
mul r0.zw, cb0[1].zzzw, l(0.000000, 0.000000, 0.500000, 0.500000)
min r1.x, r0.w, r0.z
mad r0.zw, v1.xxxy, cb0[1].zzzw, -r0.zzzw
div r0.zw, r0.zzzw, r1.xxxx
mul r0.zw, r0.zzzw, r0.zzzw
add r0.z, r0.w, r0.z
lt r0.z, l(1.000000), r0.z
if_nz r0.z
mov o0.xyzw, l(0,0,0,0)
ret
endif
else
uge r0.z, cb0[2].x, l(1)
if_nz r0.z
lt r0.zw, r0.yyyx, cb0[1].yyyy
and r1.x, r0.z, r0.w
add r2.xy, -cb0[1].yyyy, cb0[1].zwzz
lt r0.xy, r2.xyxx, r0.xyxx
and r0.zw, r0.zzzw, r0.xxxy
and r3.z, r0.y, r0.x
and r3.xy, r2.xyxx, r3.zzzz
mov r2.z, cb0[1].y
mov r2.w, l(-1)
movc r0.xyw, r0.wwww, r2.zyzw, r3.xyxz
movc r0.xyz, r0.zzzz, r2.xzwx, r0.xywx
movc r0.xyz, r1.xxxx, r2.zzwz, r0.xyzx
if_nz r0.z
mad r0.xy, v1.xyxx, cb0[1].zwzz, -r0.xyxx
mul r0.xy, r0.xyxx, r0.xyxx
add r0.x, r0.y, r0.x
mul r0.y, cb0[1].y, cb0[1].y
lt r0.x, r0.y, r0.x
if_nz r0.x
mov o0.xyzw, l(0,0,0,0)
ret
endif
endif
endif
endif
if_nz cb0[2].y
sample_indexable(texture2d)(float,float,float,float) r0.x, v1.xyxx, t2.xyzw, s0
mov_sat r0.x, r0.x
mad r0.yz, v1.xxyx, cb0[0].zzwz, cb0[0].xxyx
sample_indexable(texture2d)(float,float,float,float) r0.yzw, r0.yzyy, t0.wxyz, s0
max r0.yzw, r0.yyzw, l(0.000000, 0.001000, 0.001000, 0.001000)
log r0.yzw, r0.yyzw
mul r0.yzw, r0.yyzw, cb0[1].xxxx
exp r0.yzw, r0.yyzw
sample_indexable(texture2d)(float,float,float,float) r1.xyz, v1.xyxx, t1.xyzw, s0
add r0.yzw, r0.yyzw, -r1.xxyz
mad o0.xyz, r0.xxxx, r0.yzwy, r1.xyzx
mov o0.w, l(1.000000)
ret
else
sample_indexable(texture2d)(float,float,float,float) r0.xyz, v1.xyxx, t1.xyzw, s0
mov o0.xyz, r0.xyzx
mov o0.w, l(1.000000)
ret
endif
ret
// Approximately 63 instruction slots used
#endif
const BYTE g_WebcamCompositePS[] =
{
68, 88, 66, 67, 78, 221,
134, 205, 221, 100, 55, 97,
108, 36, 219, 137, 244, 189,
76, 224, 1, 0, 0, 0,
108, 11, 0, 0, 5, 0,
0, 0, 52, 0, 0, 0,
208, 3, 0, 0, 40, 4,
0, 0, 92, 4, 0, 0,
208, 10, 0, 0, 82, 68,
69, 70, 148, 3, 0, 0,
1, 0, 0, 0, 24, 1,
0, 0, 5, 0, 0, 0,
60, 0, 0, 0, 0, 5,
255, 255, 0, 1, 0, 0,
108, 3, 0, 0, 82, 68,
49, 49, 60, 0, 0, 0,
24, 0, 0, 0, 32, 0,
0, 0, 40, 0, 0, 0,
36, 0, 0, 0, 12, 0,
0, 0, 0, 0, 0, 0,
220, 0, 0, 0, 3, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 1, 0,
0, 0, 233, 0, 0, 0,
2, 0, 0, 0, 5, 0,
0, 0, 4, 0, 0, 0,
255, 255, 255, 255, 0, 0,
0, 0, 1, 0, 0, 0,
13, 0, 0, 0, 243, 0,
0, 0, 2, 0, 0, 0,
5, 0, 0, 0, 4, 0,
0, 0, 255, 255, 255, 255,
1, 0, 0, 0, 1, 0,
0, 0, 13, 0, 0, 0,
251, 0, 0, 0, 2, 0,
0, 0, 5, 0, 0, 0,
4, 0, 0, 0, 255, 255,
255, 255, 2, 0, 0, 0,
1, 0, 0, 0, 13, 0,
0, 0, 3, 1, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
1, 0, 0, 0, 66, 105,
108, 105, 110, 101, 97, 114,
83, 97, 109, 112, 0, 67,
97, 109, 101, 114, 97, 84,
101, 120, 0, 66, 108, 117,
114, 84, 101, 120, 0, 77,
97, 115, 107, 84, 101, 120,
0, 67, 111, 109, 112, 111,
115, 105, 116, 101, 67, 111,
110, 115, 116, 97, 110, 116,
115, 0, 171, 171, 3, 1,
0, 0, 9, 0, 0, 0,
48, 1, 0, 0, 48, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 152, 2,
0, 0, 0, 0, 0, 0,
8, 0, 0, 0, 2, 0,
0, 0, 172, 2, 0, 0,
0, 0, 0, 0, 255, 255,
255, 255, 0, 0, 0, 0,
255, 255, 255, 255, 0, 0,
0, 0, 208, 2, 0, 0,
8, 0, 0, 0, 8, 0,
0, 0, 2, 0, 0, 0,
172, 2, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255,
0, 0, 0, 0, 255, 255,
255, 255, 0, 0, 0, 0,
218, 2, 0, 0, 16, 0,
0, 0, 4, 0, 0, 0,
2, 0, 0, 0, 232, 2,
0, 0, 0, 0, 0, 0,
255, 255, 255, 255, 0, 0,
0, 0, 255, 255, 255, 255,
0, 0, 0, 0, 12, 3,
0, 0, 20, 0, 0, 0,
4, 0, 0, 0, 2, 0,
0, 0, 232, 2, 0, 0,
0, 0, 0, 0, 255, 255,
255, 255, 0, 0, 0, 0,
255, 255, 255, 255, 0, 0,
0, 0, 25, 3, 0, 0,
24, 0, 0, 0, 4, 0,
0, 0, 2, 0, 0, 0,
232, 2, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255,
0, 0, 0, 0, 255, 255,
255, 255, 0, 0, 0, 0,
33, 3, 0, 0, 28, 0,
0, 0, 4, 0, 0, 0,
2, 0, 0, 0, 232, 2,
0, 0, 0, 0, 0, 0,
255, 255, 255, 255, 0, 0,
0, 0, 255, 255, 255, 255,
0, 0, 0, 0, 41, 3,
0, 0, 32, 0, 0, 0,
4, 0, 0, 0, 2, 0,
0, 0, 60, 3, 0, 0,
0, 0, 0, 0, 255, 255,
255, 255, 0, 0, 0, 0,
255, 255, 255, 255, 0, 0,
0, 0, 96, 3, 0, 0,
36, 0, 0, 0, 4, 0,
0, 0, 2, 0, 0, 0,
60, 3, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255,
0, 0, 0, 0, 255, 255,
255, 255, 0, 0, 0, 0,
104, 3, 0, 0, 40, 0,
0, 0, 8, 0, 0, 0,
0, 0, 0, 0, 172, 2,
0, 0, 0, 0, 0, 0,
255, 255, 255, 255, 0, 0,
0, 0, 255, 255, 255, 255,
0, 0, 0, 0, 67, 114,
111, 112, 79, 102, 102, 115,
101, 116, 0, 102, 108, 111,
97, 116, 50, 0, 171, 171,
1, 0, 3, 0, 1, 0,
2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 163, 2, 0, 0,
67, 114, 111, 112, 83, 99,
97, 108, 101, 0, 71, 97,
109, 109, 97, 0, 102, 108,
111, 97, 116, 0, 171, 171,
0, 0, 3, 0, 1, 0,
1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 224, 2, 0, 0,
67, 111, 114, 110, 101, 114,
82, 97, 100, 105, 117, 115,
0, 79, 117, 116, 112, 117,
116, 87, 0, 79, 117, 116,
112, 117, 116, 72, 0, 83,
104, 97, 112, 101, 84, 121,
112, 101, 0, 100, 119, 111,
114, 100, 0, 171, 171, 171,
0, 0, 19, 0, 1, 0,
1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 51, 3, 0, 0,
72, 97, 115, 77, 97, 115,
107, 0, 80, 97, 100, 0,
77, 105, 99, 114, 111, 115,
111, 102, 116, 32, 40, 82,
41, 32, 72, 76, 83, 76,
32, 83, 104, 97, 100, 101,
114, 32, 67, 111, 109, 112,
105, 108, 101, 114, 32, 49,
48, 46, 49, 0, 73, 83,
71, 78, 80, 0, 0, 0,
2, 0, 0, 0, 8, 0,
0, 0, 56, 0, 0, 0,
0, 0, 0, 0, 1, 0,
0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 15, 0,
0, 0, 68, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 3, 0, 0, 0,
1, 0, 0, 0, 3, 3,
0, 0, 83, 86, 95, 80,
79, 83, 73, 84, 73, 79,
78, 0, 84, 69, 88, 67,
79, 79, 82, 68, 0, 171,
171, 171, 79, 83, 71, 78,
44, 0, 0, 0, 1, 0,
0, 0, 8, 0, 0, 0,
32, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0,
0, 0, 15, 0, 0, 0,
83, 86, 95, 84, 65, 82,
71, 69, 84, 0, 171, 171,
83, 72, 69, 88, 108, 6,
0, 0, 80, 0, 0, 0,
155, 1, 0, 0, 106, 8,
0, 1, 89, 0, 0, 4,
70, 142, 32, 0, 0, 0,
0, 0, 3, 0, 0, 0,
90, 0, 0, 3, 0, 96,
16, 0, 0, 0, 0, 0,
88, 24, 0, 4, 0, 112,
16, 0, 0, 0, 0, 0,
85, 85, 0, 0, 88, 24,
0, 4, 0, 112, 16, 0,
1, 0, 0, 0, 85, 85,
0, 0, 88, 24, 0, 4,
0, 112, 16, 0, 2, 0,
0, 0, 85, 85, 0, 0,
98, 16, 0, 3, 50, 16,
16, 0, 1, 0, 0, 0,
101, 0, 0, 3, 242, 32,
16, 0, 0, 0, 0, 0,
104, 0, 0, 2, 4, 0,
0, 0, 56, 0, 0, 8,
50, 0, 16, 0, 0, 0,
0, 0, 70, 16, 16, 0,
1, 0, 0, 0, 230, 138,
32, 0, 0, 0, 0, 0,
1, 0, 0, 0, 32, 0,
0, 8, 66, 0, 16, 0,
0, 0, 0, 0, 10, 128,
32, 0, 0, 0, 0, 0,
2, 0, 0, 0, 1, 64,
0, 0, 3, 0, 0, 0,
31, 0, 4, 3, 42, 0,
16, 0, 0, 0, 0, 0,
56, 0, 0, 11, 194, 0,
16, 0, 0, 0, 0, 0,
166, 142, 32, 0, 0, 0,
0, 0, 1, 0, 0, 0,
2, 64, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 63, 0, 0,
0, 63, 51, 0, 0, 7,
18, 0, 16, 0, 1, 0,
0, 0, 58, 0, 16, 0,
0, 0, 0, 0, 42, 0,
16, 0, 0, 0, 0, 0,
50, 0, 0, 11, 194, 0,
16, 0, 0, 0, 0, 0,
6, 20, 16, 0, 1, 0,
0, 0, 166, 142, 32, 0,
0, 0, 0, 0, 1, 0,
0, 0, 166, 14, 16, 128,
65, 0, 0, 0, 0, 0,
0, 0, 14, 0, 0, 7,
194, 0, 16, 0, 0, 0,
0, 0, 166, 14, 16, 0,
0, 0, 0, 0, 6, 0,
16, 0, 1, 0, 0, 0,
56, 0, 0, 7, 194, 0,
16, 0, 0, 0, 0, 0,
166, 14, 16, 0, 0, 0,
0, 0, 166, 14, 16, 0,
0, 0, 0, 0, 0, 0,
0, 7, 66, 0, 16, 0,
0, 0, 0, 0, 58, 0,
16, 0, 0, 0, 0, 0,
42, 0, 16, 0, 0, 0,
0, 0, 49, 0, 0, 7,
66, 0, 16, 0, 0, 0,
0, 0, 1, 64, 0, 0,
0, 0, 128, 63, 42, 0,
16, 0, 0, 0, 0, 0,
31, 0, 4, 3, 42, 0,
16, 0, 0, 0, 0, 0,
54, 0, 0, 8, 242, 32,
16, 0, 0, 0, 0, 0,
2, 64, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 62, 0, 0, 1,
21, 0, 0, 1, 18, 0,
0, 1, 80, 0, 0, 8,
66, 0, 16, 0, 0, 0,
0, 0, 10, 128, 32, 0,
0, 0, 0, 0, 2, 0,
0, 0, 1, 64, 0, 0,
1, 0, 0, 0, 31, 0,
4, 3, 42, 0, 16, 0,
0, 0, 0, 0, 49, 0,
0, 8, 194, 0, 16, 0,
0, 0, 0, 0, 86, 1,
16, 0, 0, 0, 0, 0,
86, 133, 32, 0, 0, 0,
0, 0, 1, 0, 0, 0,
1, 0, 0, 7, 18, 0,
16, 0, 1, 0, 0, 0,
42, 0, 16, 0, 0, 0,
0, 0, 58, 0, 16, 0,
0, 0, 0, 0, 0, 0,
0, 10, 50, 0, 16, 0,
2, 0, 0, 0, 86, 133,
32, 128, 65, 0, 0, 0,
0, 0, 0, 0, 1, 0,
0, 0, 230, 138, 32, 0,
0, 0, 0, 0, 1, 0,
0, 0, 49, 0, 0, 7,
50, 0, 16, 0, 0, 0,
0, 0, 70, 0, 16, 0,
2, 0, 0, 0, 70, 0,
16, 0, 0, 0, 0, 0,
1, 0, 0, 7, 194, 0,
16, 0, 0, 0, 0, 0,
166, 14, 16, 0, 0, 0,
0, 0, 6, 4, 16, 0,
0, 0, 0, 0, 1, 0,
0, 7, 66, 0, 16, 0,
3, 0, 0, 0, 26, 0,
16, 0, 0, 0, 0, 0,
10, 0, 16, 0, 0, 0,
0, 0, 1, 0, 0, 7,
50, 0, 16, 0, 3, 0,
0, 0, 70, 0, 16, 0,
2, 0, 0, 0, 166, 10,
16, 0, 3, 0, 0, 0,
54, 0, 0, 6, 66, 0,
16, 0, 2, 0, 0, 0,
26, 128, 32, 0, 0, 0,
0, 0, 1, 0, 0, 0,
54, 0, 0, 5, 130, 0,
16, 0, 2, 0, 0, 0,
1, 64, 0, 0, 255, 255,
255, 255, 55, 0, 0, 9,
178, 0, 16, 0, 0, 0,
0, 0, 246, 15, 16, 0,
0, 0, 0, 0, 102, 14,
16, 0, 2, 0, 0, 0,
70, 8, 16, 0, 3, 0,
0, 0, 55, 0, 0, 9,
114, 0, 16, 0, 0, 0,
0, 0, 166, 10, 16, 0,
0, 0, 0, 0, 134, 3,
16, 0, 2, 0, 0, 0,
70, 3, 16, 0, 0, 0,
0, 0, 55, 0, 0, 9,
114, 0, 16, 0, 0, 0,
0, 0, 6, 0, 16, 0,
1, 0, 0, 0, 166, 11,
16, 0, 2, 0, 0, 0,
70, 2, 16, 0, 0, 0,
0, 0, 31, 0, 4, 3,
42, 0, 16, 0, 0, 0,
0, 0, 50, 0, 0, 11,
50, 0, 16, 0, 0, 0,
0, 0, 70, 16, 16, 0,
1, 0, 0, 0, 230, 138,
32, 0, 0, 0, 0, 0,
1, 0, 0, 0, 70, 0,
16, 128, 65, 0, 0, 0,
0, 0, 0, 0, 56, 0,
0, 7, 50, 0, 16, 0,
0, 0, 0, 0, 70, 0,
16, 0, 0, 0, 0, 0,
70, 0, 16, 0, 0, 0,
0, 0, 0, 0, 0, 7,
18, 0, 16, 0, 0, 0,
0, 0, 26, 0, 16, 0,
0, 0, 0, 0, 10, 0,
16, 0, 0, 0, 0, 0,
56, 0, 0, 9, 34, 0,
16, 0, 0, 0, 0, 0,
26, 128, 32, 0, 0, 0,
0, 0, 1, 0, 0, 0,
26, 128, 32, 0, 0, 0,
0, 0, 1, 0, 0, 0,
49, 0, 0, 7, 18, 0,
16, 0, 0, 0, 0, 0,
26, 0, 16, 0, 0, 0,
0, 0, 10, 0, 16, 0,
0, 0, 0, 0, 31, 0,
4, 3, 10, 0, 16, 0,
0, 0, 0, 0, 54, 0,
0, 8, 242, 32, 16, 0,
0, 0, 0, 0, 2, 64,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
62, 0, 0, 1, 21, 0,
0, 1, 21, 0, 0, 1,
21, 0, 0, 1, 21, 0,
0, 1, 31, 0, 4, 4,
26, 128, 32, 0, 0, 0,
0, 0, 2, 0, 0, 0,
69, 0, 0, 139, 194, 0,
0, 128, 67, 85, 21, 0,
18, 0, 16, 0, 0, 0,
0, 0, 70, 16, 16, 0,
1, 0, 0, 0, 70, 126,
16, 0, 2, 0, 0, 0,
0, 96, 16, 0, 0, 0,
0, 0, 54, 32, 0, 5,
18, 0, 16, 0, 0, 0,
0, 0, 10, 0, 16, 0,
0, 0, 0, 0, 50, 0,
0, 11, 98, 0, 16, 0,
0, 0, 0, 0, 6, 17,
16, 0, 1, 0, 0, 0,
166, 139, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
6, 129, 32, 0, 0, 0,
0, 0, 0, 0, 0, 0,
69, 0, 0, 139, 194, 0,
0, 128, 67, 85, 21, 0,
226, 0, 16, 0, 0, 0,
0, 0, 150, 5, 16, 0,
0, 0, 0, 0, 54, 121,
16, 0, 0, 0, 0, 0,
0, 96, 16, 0, 0, 0,
0, 0, 52, 0, 0, 10,
226, 0, 16, 0, 0, 0,
0, 0, 86, 14, 16, 0,
0, 0, 0, 0, 2, 64,
0, 0, 0, 0, 0, 0,
111, 18, 131, 58, 111, 18,
131, 58, 111, 18, 131, 58,
47, 0, 0, 5, 226, 0,
16, 0, 0, 0, 0, 0,
86, 14, 16, 0, 0, 0,
0, 0, 56, 0, 0, 8,
226, 0, 16, 0, 0, 0,
0, 0, 86, 14, 16, 0,
0, 0, 0, 0, 6, 128,
32, 0, 0, 0, 0, 0,
1, 0, 0, 0, 25, 0,
0, 5, 226, 0, 16, 0,
0, 0, 0, 0, 86, 14,
16, 0, 0, 0, 0, 0,
69, 0, 0, 139, 194, 0,
0, 128, 67, 85, 21, 0,
114, 0, 16, 0, 1, 0,
0, 0, 70, 16, 16, 0,
1, 0, 0, 0, 70, 126,
16, 0, 1, 0, 0, 0,
0, 96, 16, 0, 0, 0,
0, 0, 0, 0, 0, 8,
226, 0, 16, 0, 0, 0,
0, 0, 86, 14, 16, 0,
0, 0, 0, 0, 6, 9,
16, 128, 65, 0, 0, 0,
1, 0, 0, 0, 50, 0,
0, 9, 114, 32, 16, 0,
0, 0, 0, 0, 6, 0,
16, 0, 0, 0, 0, 0,
150, 7, 16, 0, 0, 0,
0, 0, 70, 2, 16, 0,
1, 0, 0, 0, 54, 0,
0, 5, 130, 32, 16, 0,
0, 0, 0, 0, 1, 64,
0, 0, 0, 0, 128, 63,
62, 0, 0, 1, 18, 0,
0, 1, 69, 0, 0, 139,
194, 0, 0, 128, 67, 85,
21, 0, 114, 0, 16, 0,
0, 0, 0, 0, 70, 16,
16, 0, 1, 0, 0, 0,
70, 126, 16, 0, 1, 0,
0, 0, 0, 96, 16, 0,
0, 0, 0, 0, 54, 0,
0, 5, 114, 32, 16, 0,
0, 0, 0, 0, 70, 2,
16, 0, 0, 0, 0, 0,
54, 0, 0, 5, 130, 32,
16, 0, 0, 0, 0, 0,
1, 64, 0, 0, 0, 0,
128, 63, 62, 0, 0, 1,
21, 0, 0, 1, 62, 0,
0, 1, 83, 84, 65, 84,
148, 0, 0, 0, 63, 0,
0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 23, 0, 0, 0,
1, 0, 0, 0, 5, 0,
0, 0, 7, 0, 0, 0,
5, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 8, 0, 0, 0,
3, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0
};

View File

@@ -1,162 +0,0 @@
#if 0
//
// Generated by Microsoft (R) HLSL Shader Compiler 10.1
//
//
//
// Input signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_VertexID 0 x 0 VERTID uint x
//
//
// Output signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_POSITION 0 xyzw 0 POS float xyzw
// TEXCOORD 0 xy 1 NONE float xy
//
vs_5_0
dcl_globalFlags refactoringAllowed
dcl_input_sgv v0.x, vertex_id
dcl_output_siv o0.xyzw, position
dcl_output o1.xy
dcl_temps 1
bfi r0.x, l(1), l(1), v0.x, l(0)
and r0.z, v0.x, l(2)
utof r0.xy, r0.xzxx
mad o0.xy, r0.xyxx, l(2.000000, -2.000000, 0.000000, 0.000000), l(-1.000000, 1.000000, 0.000000, 0.000000)
mov o1.xy, r0.xyxx
mov o0.zw, l(0,0,0,1.000000)
ret
// Approximately 7 instruction slots used
#endif
const BYTE g_WebcamCompositeVS[] =
{
68, 88, 66, 67, 94, 195,
253, 40, 165, 172, 45, 84,
30, 136, 47, 40, 247, 112,
58, 27, 1, 0, 0, 0,
224, 2, 0, 0, 5, 0,
0, 0, 52, 0, 0, 0,
160, 0, 0, 0, 212, 0,
0, 0, 44, 1, 0, 0,
68, 2, 0, 0, 82, 68,
69, 70, 100, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
60, 0, 0, 0, 0, 5,
254, 255, 0, 1, 0, 0,
60, 0, 0, 0, 82, 68,
49, 49, 60, 0, 0, 0,
24, 0, 0, 0, 32, 0,
0, 0, 40, 0, 0, 0,
36, 0, 0, 0, 12, 0,
0, 0, 0, 0, 0, 0,
77, 105, 99, 114, 111, 115,
111, 102, 116, 32, 40, 82,
41, 32, 72, 76, 83, 76,
32, 83, 104, 97, 100, 101,
114, 32, 67, 111, 109, 112,
105, 108, 101, 114, 32, 49,
48, 46, 49, 0, 73, 83,
71, 78, 44, 0, 0, 0,
1, 0, 0, 0, 8, 0,
0, 0, 32, 0, 0, 0,
0, 0, 0, 0, 6, 0,
0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 1, 1,
0, 0, 83, 86, 95, 86,
101, 114, 116, 101, 120, 73,
68, 0, 79, 83, 71, 78,
80, 0, 0, 0, 2, 0,
0, 0, 8, 0, 0, 0,
56, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0,
3, 0, 0, 0, 0, 0,
0, 0, 15, 0, 0, 0,
68, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 1, 0,
0, 0, 3, 12, 0, 0,
83, 86, 95, 80, 79, 83,
73, 84, 73, 79, 78, 0,
84, 69, 88, 67, 79, 79,
82, 68, 0, 171, 171, 171,
83, 72, 69, 88, 16, 1,
0, 0, 80, 0, 1, 0,
68, 0, 0, 0, 106, 8,
0, 1, 96, 0, 0, 4,
18, 16, 16, 0, 0, 0,
0, 0, 6, 0, 0, 0,
103, 0, 0, 4, 242, 32,
16, 0, 0, 0, 0, 0,
1, 0, 0, 0, 101, 0,
0, 3, 50, 32, 16, 0,
1, 0, 0, 0, 104, 0,
0, 2, 1, 0, 0, 0,
140, 0, 0, 11, 18, 0,
16, 0, 0, 0, 0, 0,
1, 64, 0, 0, 1, 0,
0, 0, 1, 64, 0, 0,
1, 0, 0, 0, 10, 16,
16, 0, 0, 0, 0, 0,
1, 64, 0, 0, 0, 0,
0, 0, 1, 0, 0, 7,
66, 0, 16, 0, 0, 0,
0, 0, 10, 16, 16, 0,
0, 0, 0, 0, 1, 64,
0, 0, 2, 0, 0, 0,
86, 0, 0, 5, 50, 0,
16, 0, 0, 0, 0, 0,
134, 0, 16, 0, 0, 0,
0, 0, 50, 0, 0, 15,
50, 32, 16, 0, 0, 0,
0, 0, 70, 0, 16, 0,
0, 0, 0, 0, 2, 64,
0, 0, 0, 0, 0, 64,
0, 0, 0, 192, 0, 0,
0, 0, 0, 0, 0, 0,
2, 64, 0, 0, 0, 0,
128, 191, 0, 0, 128, 63,
0, 0, 0, 0, 0, 0,
0, 0, 54, 0, 0, 5,
50, 32, 16, 0, 1, 0,
0, 0, 70, 0, 16, 0,
0, 0, 0, 0, 54, 0,
0, 8, 194, 32, 16, 0,
0, 0, 0, 0, 2, 64,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 128, 63,
62, 0, 0, 1, 83, 84,
65, 84, 148, 0, 0, 0,
7, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0
};

View File

@@ -121,7 +121,7 @@ FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
DEFPUSHBUTTON "OK",IDOK,184,308,50,14
PUSHBUTTON "Cancel",IDCANCEL,241,308,50,14
LTEXT "ZoomIt v12.1",IDC_VERSION,42,7,73,10
LTEXT "ZoomIt v12.0",IDC_VERSION,42,7,73,10
LTEXT "Copyright \251 2006-2026 Mark Russinovich",IDC_COPYRIGHT,42,17,251,8
CONTROL "<a HREF=""https://www.sysinternals.com"">Sysinternals - www.sysinternals.com</a>",IDC_LINK,
"SysLink",WS_TABSTOP,42,26,150,9
@@ -267,52 +267,33 @@ RECORD DIALOGEX 0, 0, 263, 228
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_RECORD_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,65,59,80,12
RTEXT "Record Toggle:",IDC_STATIC,7,62,54,8
CONTROL "",IDC_RECORD_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,61,71,80,12
LTEXT "Record Toggle:",IDC_STATIC,7,73,54,8
LTEXT "Record video of the unzoomed live screen or a static zoomed session by entering the recording hot key and finish the recording by entering it again. ",IDC_STATIC,7,7,248,22
RTEXT "Scaling:",IDC_STATIC,36,79,26,8
COMBOBOX IDC_RECORD_SCALING,65,78,26,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OEMCONVERT | CBS_SORT | WS_VSCROLL | WS_TABSTOP
CONTROL "16:9:",IDC_RECORD_ASPECT_RATIO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,94,79,30,10
RTEXT "Format:",IDC_STATIC,36,97,26,8
COMBOBOX IDC_RECORD_FORMAT,65,96,60,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OEMCONVERT | WS_VSCROLL | WS_TABSTOP
LTEXT "Frame Rate:",IDC_STATIC,134,79,44,8,NOT WS_VISIBLE
COMBOBOX IDC_RECORD_FRAME_RATE,177,78,42,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OEMCONVERT | CBS_SORT | NOT WS_VISIBLE | WS_VSCROLL | WS_TABSTOP
LTEXT "To crop the portion of the screen that will be recorded, enter the hotkey with the Shift key in the opposite mode. ",IDC_STATIC,7,7,245,18
LTEXT "To record a specific window, enter the hotkey with the Alt key in the opposite mode.",IDC_STATIC,7,38,251,19
CONTROL "Capture &system audio:",IDC_CAPTURE_SYSTEM_AUDIO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_RIGHT | WS_TABSTOP,7,115,90,10
CONTROL "&Capture audio input:",IDC_CAPTURE_AUDIO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_RIGHT | WS_TABSTOP,11,133,86,10
CONTROL "Mono:",IDC_MIC_MONO_MIX,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_RIGHT | WS_TABSTOP,108,147,48,10
COMBOBOX IDC_MICROPHONE,52,162,208,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Microphone:",IDC_MICROPHONE_LABEL,7,163,42,8
CONTROL "&Noise cancellation:",IDC_NOISE_CANCELLATION,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_RIGHT | WS_TABSTOP,23,147,74,10
LTEXT "Scaling:",IDC_STATIC,30,90,26,8
COMBOBOX IDC_RECORD_SCALING,61,89,26,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OEMCONVERT | CBS_SORT | WS_VSCROLL | WS_TABSTOP
CONTROL "16:9:",IDC_RECORD_ASPECT_RATIO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,93,90,30,10
LTEXT "Format:",IDC_STATIC,30,108,26,8
COMBOBOX IDC_RECORD_FORMAT,61,106,60,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OEMCONVERT | WS_VSCROLL | WS_TABSTOP
LTEXT "Frame Rate:",IDC_STATIC,135,90,44,8,NOT WS_VISIBLE
COMBOBOX IDC_RECORD_FRAME_RATE,182,89,42,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OEMCONVERT | CBS_SORT | NOT WS_VISIBLE | WS_VSCROLL | WS_TABSTOP
LTEXT "To crop the portion of the screen that will be recorded, enter the hotkey with the Shift key in the opposite mode. ",IDC_STATIC,7,29,245,18
LTEXT "To record a specific window, enter the hotkey with the Alt key in the opposite mode.",IDC_STATIC,7,48,251,19
CONTROL "Capture &system audio",IDC_CAPTURE_SYSTEM_AUDIO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,124,86,10
CONTROL "&Capture audio input:",IDC_CAPTURE_AUDIO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,125,124,83,10
COMBOBOX IDC_MICROPHONE,81,137,128,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Microphone:",IDC_MICROPHONE_LABEL,34,139,47,8
CONTROL "Show &webcam overlay (Ctrl+C toggles)",IDC_WEBCAM_OVERLAY,
"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,181,148,10
PUSHBUTTON "Webcam S&ettings...",IDC_WEBCAM_SETTINGS,192,180,68,14
PUSHBUTTON "&Trim",IDC_TRIM_FILE,207,209,53,14
END
WEBCAM_SETTINGS DIALOGEX 0, 0, 220, 163
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Webcam Settings"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "Camera:",IDC_WEBCAM_DEVICE_LABEL,14,10,28,8
COMBOBOX IDC_WEBCAM_DEVICE,50,8,158,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Position:",IDC_WEBCAM_POSITION_LABEL,14,28,32,8
COMBOBOX IDC_WEBCAM_POSITION,50,26,65,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Size:",IDC_WEBCAM_SIZE_LABEL,125,28,20,8
COMBOBOX IDC_WEBCAM_SIZE,148,26,60,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Shape:",IDC_WEBCAM_SHAPE_LABEL,14,46,24,8
COMBOBOX IDC_WEBCAM_SHAPE,50,44,80,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Background:",IDC_WEBCAM_BG_LABEL,14,64,44,8
COMBOBOX IDC_WEBCAM_BG_MODE,60,62,55,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
EDITTEXT IDC_WEBCAM_BG_IMAGE,117,62,83,12,ES_AUTOHSCROLL | ES_READONLY
PUSHBUTTON "...",IDC_WEBCAM_BG_BROWSE,202,62,14,12
LTEXT "Brightness:",IDC_WEBCAM_BRIGHTNESS_LABEL,14,82,44,8
CONTROL "",IDC_WEBCAM_BRIGHTNESS_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | TBS_NOTICKS | TBS_TOOLTIPS | WS_TABSTOP,60,80,148,15
LTEXT "Uses MediaPipe SelfieSegmentation (Apache 2.0)",IDC_THIRDPARTY_NOTICES,14,102,200,8
DEFPUSHBUTTON "OK",IDOK,108,142,50,14
PUSHBUTTON "Cancel",IDCANCEL,162,142,50,14
"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,153,148,10
LTEXT "Camera:",IDC_WEBCAM_DEVICE_LABEL,46,167,28,8
COMBOBOX IDC_WEBCAM_DEVICE,82,165,127,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Position:",IDC_WEBCAM_POSITION_LABEL,33,185,32,8
COMBOBOX IDC_WEBCAM_POSITION,64,183,55,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Size:",IDC_WEBCAM_SIZE_LABEL,137,185,20,8
COMBOBOX IDC_WEBCAM_SIZE,159,183,50,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Shape:",IDC_WEBCAM_SHAPE_LABEL,33,201,24,8
COMBOBOX IDC_WEBCAM_SHAPE,64,199,80,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "&Trim",IDC_TRIM_FILE,207,207,53,14
END
SNIP DIALOGEX 0, 0, 260, 80

View File

@@ -90,7 +90,7 @@
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(InterPlatformDir)</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;windowscodecs.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<RandomizedBaseAddress>true</RandomizedBaseAddress>
@@ -112,7 +112,7 @@
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(MSBuildThisFileDirectory)..\ZoomItBreak\$(Platform)\$(Configuration)\</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;windowscodecs.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<RandomizedBaseAddress>true</RandomizedBaseAddress>
@@ -135,7 +135,7 @@
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(MSBuildThisFileDirectory)..\ZoomItBreak\$(Platform)\$(Configuration)\</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;windowscodecs.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<FixedBaseAddress>
@@ -156,7 +156,7 @@
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(InterPlatformDir)</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;windowscodecs.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
@@ -177,7 +177,7 @@
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(MSBuildThisFileDirectory)..\ZoomItBreak\$(Platform)\$(Configuration)\</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;windowscodecs.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACUIAccess>true</UACUIAccess>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
@@ -199,7 +199,7 @@
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\..\common\version;$(MSBuildThisFileDirectory)PowerToys;$(MSBuildThisFileDirectory)..\ZoomItBreak\$(Platform)\$(Configuration)\</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;windowscodecs.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACUIAccess>true</UACUIAccess>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
@@ -257,7 +257,6 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="GifRecordingSession.cpp" />
<ClCompile Include="NoiseSuppressor.cpp" />
<ClCompile Include="PanoramaCapture.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Use</PrecompiledHeader>
@@ -267,128 +266,6 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Use</PrecompiledHeader>
</ClCompile>
<ClCompile Include="pch.cpp" />
<ClCompile Include="rnnoise\celt_lpc.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">26451;4100;4091;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">26451;4100;4091;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">26451;4100;4091;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">26451;4100;4091;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">26451;4100;4091;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">26451;4100;4091;4245</DisableSpecificWarnings>
</ClCompile>
<ClCompile Include="rnnoise\denoise.c">
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">26451;4100;4091;4244;4245;4305</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">26451;4100;4091;4244;4245;4305</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">26451;4100;4091;4244;4245;4305</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">26451;4100;4091;4244;4245;4305</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">26451;4100;4091;4244;4245;4305</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">26451;4100;4091;4244;4245;4305</DisableSpecificWarnings>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="rnnoise\kiss_fft.c">
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="rnnoise\nnet.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
</ClCompile>
<ClCompile Include="rnnoise\nnet_default.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
</ClCompile>
<ClCompile Include="rnnoise\parse_lpcnet_weights.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="rnnoise\pitch.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">26451;4100;4091;4244;4245</DisableSpecificWarnings>
</ClCompile>
<ClCompile Include="rnnoise\rnn.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="rnnoise\rnnoise_data_little.c">
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">26451;4100;4091;4245;4305</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">26451;4100;4091;4245;4305</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">26451;4100;4091;4245;4305</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">26451;4100;4091;4245;4305</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">26451;4100;4091;4245;4305</DisableSpecificWarnings>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">26451;4100;4091;4245;4305</DisableSpecificWarnings>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="rnnoise\rnnoise_tables.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="SelectRectangle.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Use</PrecompiledHeader>
@@ -437,14 +314,6 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Use</PrecompiledHeader>
</ClCompile>
<ClCompile Include="BackgroundBlur.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Use</PrecompiledHeader>
</ClCompile>
<ClCompile Include="WebcamPreviewWindow.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Use</PrecompiledHeader>
@@ -469,21 +338,16 @@
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\common\sysinternals\Eula\Eula.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)..\ZoomItModuleInterface\Trace.h" />
<ClInclude Include="GifRecordingSession.h" />
<ClInclude Include="NoiseSuppressor.h" />
<ClInclude Include="PanoramaCapture.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="Registry.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="rnnoise\nnet_arch.h" />
<ClInclude Include="rnnoise\rnnoise.h" />
<ClInclude Include="rnnoise\vec.h" />
<ClInclude Include="SelectRectangle.h" />
<ClInclude Include="Utility.h" />
<ClInclude Include="DemoType.h" />
<ClInclude Include="VersionHelper.h" />
<ClInclude Include="VideoRecordingSession.h" />
<ClInclude Include="WebcamCapture.h" />
<ClInclude Include="BackgroundBlur.h" />
<ClInclude Include="WebcamPreviewWindow.h" />
<ClInclude Include="ZoomIt.h" />
<ClInclude Include="ZoomItSettings.h" />

View File

@@ -13,12 +13,6 @@
<UniqueIdentifier>{e1fa606f-a2e6-40c8-8779-8ca1813d9f01}</UniqueIdentifier>
<Extensions>ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
</Filter>
<Filter Include="Source Files\rnnoise">
<UniqueIdentifier>{c1cc2820-c3a4-413d-b5d8-0d2034de2474}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\rnnoise">
<UniqueIdentifier>{7795b908-e073-46ed-b209-c07ac0324adb}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Zoomit.cpp">
@@ -72,48 +66,6 @@
<ClCompile Include="..\ZoomItBreak\BreakTimer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="BackgroundBlur.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="WebcamCapture.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="WebcamPreviewWindow.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="rnnoise\celt_lpc.c">
<Filter>Source Files\rnnoise</Filter>
</ClCompile>
<ClCompile Include="rnnoise\denoise.c">
<Filter>Source Files\rnnoise</Filter>
</ClCompile>
<ClCompile Include="rnnoise\kiss_fft.c">
<Filter>Source Files\rnnoise</Filter>
</ClCompile>
<ClCompile Include="rnnoise\nnet.c">
<Filter>Source Files\rnnoise</Filter>
</ClCompile>
<ClCompile Include="rnnoise\nnet_default.c">
<Filter>Source Files\rnnoise</Filter>
</ClCompile>
<ClCompile Include="rnnoise\parse_lpcnet_weights.c">
<Filter>Source Files\rnnoise</Filter>
</ClCompile>
<ClCompile Include="rnnoise\pitch.c">
<Filter>Source Files\rnnoise</Filter>
</ClCompile>
<ClCompile Include="rnnoise\rnn.c">
<Filter>Source Files\rnnoise</Filter>
</ClCompile>
<ClCompile Include="rnnoise\rnnoise_data_little.c">
<Filter>Source Files\rnnoise</Filter>
</ClCompile>
<ClCompile Include="rnnoise\rnnoise_tables.c">
<Filter>Source Files\rnnoise</Filter>
</ClCompile>
<ClCompile Include="NoiseSuppressor.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Registry.h">
@@ -167,27 +119,6 @@
<ClInclude Include="..\ZoomItBreak\BreakTimer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="BackgroundBlur.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="WebcamCapture.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="WebcamPreviewWindow.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="rnnoise\vec.h">
<Filter>Header Files\rnnoise</Filter>
</ClInclude>
<ClInclude Include="rnnoise\nnet_arch.h">
<Filter>Header Files\rnnoise</Filter>
</ClInclude>
<ClInclude Include="NoiseSuppressor.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="rnnoise\rnnoise.h">
<Filter>Header Files\rnnoise</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="appicon.ico">
@@ -219,6 +150,5 @@
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natstepfilter" />
</ItemGroup>
</Project>

View File

@@ -56,16 +56,12 @@ RecordingFormat g_RecordingFormat = RecordingFormat::MP4;
BOOLEAN g_CaptureSystemAudio = TRUE;
BOOLEAN g_CaptureAudio = FALSE;
BOOLEAN g_MicMonoMix = FALSE;
BOOLEAN g_NoiseCancellation = TRUE;
TCHAR g_MicrophoneDeviceId[MAX_PATH] = {0};
BOOLEAN g_WebcamOverlay = FALSE;
DWORD g_WebcamPosition = 3; // 0=TL, 1=TR, 2=BL, 3=BR
DWORD g_WebcamSize = 1; // 0=Small(15%), 1=Medium(25%), 2=Large(33%), 3=XLarge(50%)
DWORD g_WebcamShape = 0; // 0=Square, 1=RoundedRect, 2=RoundedSquare, 3=Circle
TCHAR g_WebcamDeviceSymLink[MAX_PATH] = {0};
DWORD g_WebcamBackgroundMode = 0; // 0=None, 1=Blur, 2=Image
TCHAR g_WebcamBackgroundImage[MAX_PATH] = {0};
DWORD g_WebcamBrightness = 50; // 0=dark, 50=neutral, 100=bright
BOOLEAN g_RecordAspectRatio = FALSE; // Lock region selection to 16:9
TCHAR g_RecordingSaveLocationBuffer[MAX_PATH] = {0};
TCHAR g_ScreenshotSaveLocationBuffer[MAX_PATH] = {0};
@@ -120,15 +116,11 @@ REG_SETTING RegSettings[] = {
{ L"CaptureSystemAudio", SETTING_TYPE_BOOLEAN, 0, &g_CaptureSystemAudio, static_cast<DOUBLE>(g_CaptureSystemAudio) },
{ L"MicMonoMix", SETTING_TYPE_BOOLEAN, 0, &g_MicMonoMix, static_cast<DOUBLE>(g_MicMonoMix) },
{ L"MicrophoneDeviceId", SETTING_TYPE_STRING, sizeof(g_MicrophoneDeviceId), g_MicrophoneDeviceId, static_cast<DOUBLE>(0) },
{ L"NoiseCancellation", SETTING_TYPE_BOOLEAN, 0, &g_NoiseCancellation, static_cast<DOUBLE>(g_NoiseCancellation) },
{ L"WebcamOverlay", SETTING_TYPE_BOOLEAN, 0, &g_WebcamOverlay, static_cast<DOUBLE>(g_WebcamOverlay) },
{ L"WebcamPosition", SETTING_TYPE_DWORD, 0, &g_WebcamPosition, static_cast<DOUBLE>(g_WebcamPosition) },
{ L"WebcamSize", SETTING_TYPE_DWORD, 0, &g_WebcamSize, static_cast<DOUBLE>(g_WebcamSize) },
{ L"WebcamShape", SETTING_TYPE_DWORD, 0, &g_WebcamShape, static_cast<DOUBLE>(g_WebcamShape) },
{ L"WebcamDeviceSymLink", SETTING_TYPE_STRING, sizeof(g_WebcamDeviceSymLink), g_WebcamDeviceSymLink, static_cast<DOUBLE>(0) },
{ L"WebcamBackgroundMode", SETTING_TYPE_DWORD, 0, &g_WebcamBackgroundMode, static_cast<DOUBLE>(g_WebcamBackgroundMode) },
{ L"WebcamBackgroundImage", SETTING_TYPE_STRING, sizeof(g_WebcamBackgroundImage), g_WebcamBackgroundImage, static_cast<DOUBLE>(0) },
{ L"WebcamBrightness", SETTING_TYPE_DWORD, 0, &g_WebcamBrightness, static_cast<DOUBLE>(g_WebcamBrightness) },
{ L"RecordAspectRatio", SETTING_TYPE_BOOLEAN, 0, &g_RecordAspectRatio, static_cast<DOUBLE>(g_RecordAspectRatio) },
{ L"RecordingSaveLocation", SETTING_TYPE_STRING, sizeof(g_RecordingSaveLocationBuffer), g_RecordingSaveLocationBuffer, static_cast<DOUBLE>(0) },
{ L"ScreenshotSaveLocation", SETTING_TYPE_STRING, sizeof(g_ScreenshotSaveLocationBuffer), g_ScreenshotSaveLocationBuffer, static_cast<DOUBLE>(0) },

View File

@@ -21,6 +21,7 @@
#include "PanoramaCapture.h"
#include <wtsapi32.h>
#include <tlhelp32.h>
#include <limits>
#include <vector>
#ifdef __ZOOMIT_POWERTOYS__
@@ -724,7 +725,7 @@ RunningOnWin64(
// this executable.
//
//--------------------------------------------------------------------
BOOLEAN ExtractImageResource( PCTSTR ResourceName, PTCHAR TargetFile )
BOOLEAN ExtractImageResource( PTCHAR ResourceName, PTCHAR TargetFile )
{
HRSRC hResource;
HGLOBAL hImageResource;
@@ -2834,323 +2835,6 @@ INT_PTR CALLBACK AdvancedBreakProc( HWND hDlg, UINT message, WPARAM wParam, LPAR
}
//----------------------------------------------------------------------------
//
// WebcamSettingsProc
//
//----------------------------------------------------------------------------
INT_PTR CALLBACK WebcamSettingsProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
{
static std::vector<std::pair<std::wstring, std::wstring>> webcams;
static UINT currentDpi = DPI_BASELINE;
static HWND s_hBrightnessTooltip = nullptr;
switch( message ) {
case WM_INITDIALOG:
{
HICON hIcon = LoadIcon( g_hInstance, L"APPICON" );
if( hIcon )
{
SendMessage( hDlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIcon) );
SendMessage( hDlg, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hIcon) );
}
// Enumerate webcam devices
webcams.clear();
{
MFStartup( MF_VERSION, MFSTARTUP_LITE );
IMFAttributes* pAttributes = nullptr;
if( SUCCEEDED( MFCreateAttributes( &pAttributes, 1 ) ) )
{
pAttributes->SetGUID( MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID );
IMFActivate** ppDevices = nullptr;
UINT32 count = 0;
if( SUCCEEDED( MFEnumDeviceSources( pAttributes, &ppDevices, &count ) ) )
{
for( UINT32 i = 0; i < count; i++ )
{
LPWSTR symLink = nullptr, friendlyName = nullptr;
UINT32 nameLen = 0;
if( SUCCEEDED( ppDevices[i]->GetAllocatedString( MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &symLink, &nameLen ) ) &&
SUCCEEDED( ppDevices[i]->GetAllocatedString( MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &friendlyName, &nameLen ) ) )
{
webcams.emplace_back( symLink, friendlyName );
}
if( symLink ) CoTaskMemFree( symLink );
if( friendlyName ) CoTaskMemFree( friendlyName );
ppDevices[i]->Release();
}
CoTaskMemFree( ppDevices );
}
pAttributes->Release();
}
MFShutdown();
}
// Populate camera combo
SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_DEVICE ), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Default") );
{
int selection = 0;
for( size_t i = 0; i < webcams.size(); i++ )
{
SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_DEVICE ), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(webcams[i].second.c_str()) );
if( selection == 0 && wcscmp( webcams[i].first.c_str(), g_WebcamDeviceSymLink ) == 0 )
selection = static_cast<int>(i + 1);
}
SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_DEVICE ), CB_SETCURSEL, selection, 0 );
}
// Populate position combo
{
const wchar_t* positions[] = { L"Top-left", L"Top-right", L"Bottom-left", L"Bottom-right" };
for( int i = 0; i < 4; i++ )
SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_POSITION ), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(positions[i]) );
SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_POSITION ), CB_SETCURSEL, g_WebcamPosition, 0 );
}
// Populate size combo
{
const wchar_t* sizes[] = { L"Small", L"Medium", L"Large", L"X-Large", L"Full screen" };
for( int i = 0; i < 5; i++ )
SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_SIZE ), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(sizes[i]) );
SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_SIZE ), CB_SETCURSEL, g_WebcamSize, 0 );
}
// Populate shape combo
{
const wchar_t* shapes[] = { L"Rectangle", L"Rounded Rectangle", L"Rounded Square", L"Circle" };
for( int i = 0; i < 4; i++ )
SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_SHAPE ), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(shapes[i]) );
SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_SHAPE ), CB_SETCURSEL, g_WebcamShape, 0 );
}
// Populate background mode combo
{
HWND hBgMode = GetDlgItem( hDlg, IDC_WEBCAM_BG_MODE );
SendMessage( hBgMode, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"None") );
SendMessage( hBgMode, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Blur") );
SendMessage( hBgMode, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Image") );
SendMessage( hBgMode, CB_SETCURSEL, g_WebcamBackgroundMode, 0 );
SetDlgItemText( hDlg, IDC_WEBCAM_BG_IMAGE, g_WebcamBackgroundImage );
}
// Initialize brightness slider
{
HWND hSlider = GetDlgItem( hDlg, IDC_WEBCAM_BRIGHTNESS_SLIDER );
SendMessage( hSlider, TBM_SETRANGE, FALSE, MAKELONG( 0, 100 ) );
SendMessage( hSlider, TBM_SETPOS, TRUE, g_WebcamBrightness );
// Place the built-in drag tooltip (TBS_TOOLTIPS) beneath the slider.
SendMessage( hSlider, TBM_SETTIPSIDE, TBTS_BOTTOM, 0 );
// Hover tooltip that displays the current brightness when not dragging.
// TTF_SUBCLASS lets the tooltip detect hover itself; LPSTR_TEXTCALLBACK
// makes us supply the current value via TTN_GETDISPINFO.
s_hBrightnessTooltip = CreateWindowEx(
WS_EX_TOPMOST, TOOLTIPS_CLASS, nullptr,
WS_POPUP | TTS_ALWAYSTIP | TTS_NOPREFIX,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
hDlg, nullptr, g_hInstance, nullptr );
if( s_hBrightnessTooltip )
{
TOOLINFO ti = { sizeof(TOOLINFO) };
ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
ti.hwnd = hDlg;
ti.uId = reinterpret_cast<UINT_PTR>(hSlider);
ti.lpszText = LPSTR_TEXTCALLBACK;
SendMessage( s_hBrightnessTooltip, TTM_ADDTOOL, 0, reinterpret_cast<LPARAM>(&ti) );
}
}
// Set initial enabled state
{
bool isFullScreen = g_WebcamSize == 4;
EnableWindow( GetDlgItem( hDlg, IDC_WEBCAM_SHAPE_LABEL ), !isFullScreen );
EnableWindow( GetDlgItem( hDlg, IDC_WEBCAM_SHAPE ), !isFullScreen );
bool isImageMode = g_WebcamBackgroundMode == 2;
EnableWindow( GetDlgItem( hDlg, IDC_WEBCAM_BG_IMAGE ), isImageMode );
EnableWindow( GetDlgItem( hDlg, IDC_WEBCAM_BG_BROWSE ), isImageMode );
}
// DPI scaling
currentDpi = GetDpiForWindowHelper( hDlg );
if( currentDpi != DPI_BASELINE )
ScaleDialogForDpi( hDlg, currentDpi, DPI_BASELINE );
ApplyDarkModeToDialog( hDlg );
return TRUE;
}
case WM_DPICHANGED:
HandleDialogDpiChange( hDlg, wParam, lParam, currentDpi );
return TRUE;
case WM_ERASEBKGND:
if( IsDarkModeEnabled() )
{
HDC hdc = reinterpret_cast<HDC>(wParam);
RECT rc;
GetClientRect( hDlg, &rc );
FillRect( hdc, &rc, GetDarkModeBrush() );
return TRUE;
}
break;
case WM_CTLCOLORDLG:
case WM_CTLCOLORSTATIC:
case WM_CTLCOLORBTN:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORLISTBOX:
{
HDC hdc = reinterpret_cast<HDC>(wParam);
HWND hCtrl = reinterpret_cast<HWND>(lParam);
HBRUSH hBrush = HandleDarkModeCtlColor( hdc, hCtrl, message );
if( hBrush )
return reinterpret_cast<INT_PTR>(hBrush);
break;
}
case WM_NOTIFY:
{
LPNMHDR pnmh = reinterpret_cast<LPNMHDR>(lParam);
if( s_hBrightnessTooltip && pnmh->hwndFrom == s_hBrightnessTooltip )
{
if( pnmh->code == TTN_GETDISPINFO )
{
LPNMTTDISPINFO pdi = reinterpret_cast<LPNMTTDISPINFO>(lParam);
static wchar_t buf[16];
int pos = static_cast<int>(SendMessage(
GetDlgItem( hDlg, IDC_WEBCAM_BRIGHTNESS_SLIDER ), TBM_GETPOS, 0, 0 ));
_snwprintf_s( buf, _countof(buf), _TRUNCATE, L"%d", pos );
pdi->lpszText = buf;
return TRUE;
}
if( pnmh->code == TTN_SHOW )
{
// Reposition the tooltip directly beneath the slider, centered on the cursor.
HWND hSlider = GetDlgItem( hDlg, IDC_WEBCAM_BRIGHTNESS_SLIDER );
RECT rcSlider, rcTip;
GetWindowRect( hSlider, &rcSlider );
GetWindowRect( s_hBrightnessTooltip, &rcTip );
POINT pt;
GetCursorPos( &pt );
int tipW = rcTip.right - rcTip.left;
SetWindowPos( s_hBrightnessTooltip, HWND_TOPMOST,
pt.x - tipW / 2, rcSlider.bottom + 4, 0, 0,
SWP_NOSIZE | SWP_NOACTIVATE );
return TRUE;
}
}
break;
}
case WM_DESTROY:
if( s_hBrightnessTooltip )
{
DestroyWindow( s_hBrightnessTooltip );
s_hBrightnessTooltip = nullptr;
}
break;
case WM_COMMAND:
// Handle size combo change — disable shape when Full screen
if( HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_WEBCAM_SIZE )
{
bool isFullScreen = SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_SIZE ), CB_GETCURSEL, 0, 0 ) == 4;
EnableWindow( GetDlgItem( hDlg, IDC_WEBCAM_SHAPE_LABEL ), !isFullScreen );
EnableWindow( GetDlgItem( hDlg, IDC_WEBCAM_SHAPE ), !isFullScreen );
}
// Handle background mode combo change — show/hide browse controls
if( HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_WEBCAM_BG_MODE )
{
bool isImageMode = SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_BG_MODE ), CB_GETCURSEL, 0, 0 ) == 2;
EnableWindow( GetDlgItem( hDlg, IDC_WEBCAM_BG_IMAGE ), isImageMode );
EnableWindow( GetDlgItem( hDlg, IDC_WEBCAM_BG_BROWSE ), isImageMode );
}
switch( LOWORD(wParam) ) {
case IDC_WEBCAM_BG_BROWSE:
{
auto openDialog = wil::CoCreateInstance<IFileOpenDialog>( CLSID_FileOpenDialog );
FILEOPENDIALOGOPTIONS options;
if( SUCCEEDED( openDialog->GetOptions( &options ) ) )
openDialog->SetOptions( options | FOS_FORCEFILESYSTEM );
COMDLG_FILTERSPEC fileTypes[] = {
{ L"Bitmap Files (*.bmp;*.dib)", L"*.bmp;*.dib" },
{ L"PNG (*.png)", L"*.png" },
{ L"JPEG (*.jpg;*.jpeg;*.jpe;*.jfif)", L"*.jpg;*.jpeg;*.jpe;*.jfif" },
{ L"GIF (*.gif)", L"*.gif" },
{ L"All Picture Files", L"*.bmp;*.dib;*.png;*.jpg;*.jpeg;*.jpe;*.jfif;*.gif" },
{ L"All Files", L"*.*" }
};
openDialog->SetFileTypes( _countof( fileTypes ), fileTypes );
openDialog->SetFileTypeIndex( 5 );
openDialog->SetTitle( L"ZoomIt: Specify Background Image..." );
TCHAR bgFilePath[MAX_PATH], bgInitDir[MAX_PATH];
GetDlgItemText( hDlg, IDC_WEBCAM_BG_IMAGE, bgFilePath, _countof( bgFilePath ) );
if( _tcsrchr( bgFilePath, '\\' ) )
{
_tcscpy( bgInitDir, bgFilePath );
*( _tcsrchr( bgInitDir, '\\' ) + 1 ) = 0;
}
else
{
_tcscpy( bgFilePath, L"%USERPROFILE%\\Pictures" );
ExpandEnvironmentStrings( bgFilePath, bgInitDir, _countof( bgInitDir ) );
}
wil::com_ptr<IShellItem> folderItem;
if( SUCCEEDED( SHCreateItemFromParsingName( bgInitDir, nullptr, IID_PPV_ARGS( &folderItem ) ) ) )
openDialog->SetFolder( folderItem.get() );
OpenSaveDialogEvents* pEvents = new OpenSaveDialogEvents(false);
DWORD dwCookie = 0;
openDialog->Advise( pEvents, &dwCookie );
if( SUCCEEDED( openDialog->Show( hDlg ) ) )
{
wil::com_ptr<IShellItem> resultItem;
if( SUCCEEDED( openDialog->GetResult( &resultItem ) ) )
{
wil::unique_cotaskmem_string pathStr;
if( SUCCEEDED( resultItem->GetDisplayName( SIGDN_FILESYSPATH, &pathStr ) ) )
SetDlgItemText( hDlg, IDC_WEBCAM_BG_IMAGE, pathStr.get() );
}
}
openDialog->Unadvise( dwCookie );
pEvents->Release();
break;
}
case IDOK:
g_WebcamPosition = static_cast<DWORD>(SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_POSITION ), CB_GETCURSEL, 0, 0 ));
g_WebcamSize = static_cast<DWORD>(SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_SIZE ), CB_GETCURSEL, 0, 0 ));
g_WebcamShape = static_cast<DWORD>(SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_SHAPE ), CB_GETCURSEL, 0, 0 ));
g_WebcamBackgroundMode = static_cast<DWORD>(SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_BG_MODE ), CB_GETCURSEL, 0, 0 ));
GetDlgItemText( hDlg, IDC_WEBCAM_BG_IMAGE, g_WebcamBackgroundImage, MAX_PATH );
g_WebcamBrightness = static_cast<DWORD>(SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_BRIGHTNESS_SLIDER ), TBM_GETPOS, 0, 0 ));
{
int wcIndex = static_cast<int>(SendMessage( GetDlgItem( hDlg, IDC_WEBCAM_DEVICE ), CB_GETCURSEL, 0, 0 ));
_tcscpy( g_WebcamDeviceSymLink, wcIndex == 0 ? L"" : webcams[static_cast<size_t>(wcIndex) - 1].first.c_str() );
}
reg.WriteRegSettings( RegSettings );
EndDialog( hDlg, 0 );
break;
case IDCANCEL:
EndDialog( hDlg, 0 );
return TRUE;
}
break;
}
return FALSE;
}
//----------------------------------------------------------------------------
//
// OptionsTabProc
@@ -3288,33 +2972,52 @@ INT_PTR CALLBACK OptionsTabProc( HWND hDlg, UINT message,
// Enable/disable audio controls based on selection (GIF has no audio)
EnableWindow(GetDlgItem(hDlg, IDC_CAPTURE_SYSTEM_AUDIO), !isGifSelected);
EnableWindow(GetDlgItem(hDlg, IDC_CAPTURE_AUDIO), !isGifSelected);
EnableWindow(GetDlgItem(hDlg, IDC_NOISE_CANCELLATION), !isGifSelected);
EnableWindow(GetDlgItem(hDlg, IDC_MIC_MONO_MIX), !isGifSelected);
EnableWindow(GetDlgItem(hDlg, IDC_MICROPHONE_LABEL), !isGifSelected);
EnableWindow(GetDlgItem(hDlg, IDC_MICROPHONE), !isGifSelected);
// Enable/disable webcam overlay and settings button (webcam overlay is MP4-only).
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_OVERLAY), !isGifSelected);
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_SETTINGS), !isGifSelected && IsDlgButtonChecked(hDlg, IDC_WEBCAM_OVERLAY) == BST_CHECKED);
// Enable/disable webcam controls (webcam overlay is MP4-only).
// Also keep everything disabled if no webcam is present.
bool hasWebcam = SendMessage(GetDlgItem(hDlg, IDC_WEBCAM_DEVICE), CB_GETCOUNT, 0, 0) > 1;
bool webcamEnabled = hasWebcam && !isGifSelected && IsDlgButtonChecked(hDlg, IDC_WEBCAM_OVERLAY) == BST_CHECKED;
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_OVERLAY), hasWebcam && !isGifSelected);
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_DEVICE_LABEL), webcamEnabled);
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_DEVICE), webcamEnabled);
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_POSITION_LABEL), webcamEnabled);
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_POSITION), webcamEnabled);
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_SIZE_LABEL), webcamEnabled);
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_SIZE), webcamEnabled);
{
bool isFullScreen = webcamEnabled && SendMessage(GetDlgItem(hDlg, IDC_WEBCAM_SIZE), CB_GETCURSEL, 0, 0) == 4;
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_SHAPE_LABEL), webcamEnabled && !isFullScreen);
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_SHAPE), webcamEnabled && !isFullScreen);
}
}
}
if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_WEBCAM_OVERLAY) {
bool webcamEnabled = IsDlgButtonChecked(hDlg, IDC_WEBCAM_OVERLAY) == BST_CHECKED;
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_SETTINGS), webcamEnabled);
bool isGif = (g_RecordingFormat == RecordingFormat::GIF);
bool webcamEnabled = !isGif && IsDlgButtonChecked(hDlg, IDC_WEBCAM_OVERLAY) == BST_CHECKED;
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_DEVICE_LABEL), webcamEnabled);
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_DEVICE), webcamEnabled);
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_POSITION_LABEL), webcamEnabled);
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_POSITION), webcamEnabled);
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_SIZE_LABEL), webcamEnabled);
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_SIZE), webcamEnabled);
{
bool isFullScreen = webcamEnabled && SendMessage(GetDlgItem(hDlg, IDC_WEBCAM_SIZE), CB_GETCURSEL, 0, 0) == 4;
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_SHAPE_LABEL), webcamEnabled && !isFullScreen);
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_SHAPE), webcamEnabled && !isFullScreen);
}
}
if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_CAPTURE_AUDIO) {
bool micEnabled = IsDlgButtonChecked(hDlg, IDC_CAPTURE_AUDIO) == BST_CHECKED;
EnableWindow(GetDlgItem(hDlg, IDC_NOISE_CANCELLATION), micEnabled);
EnableWindow(GetDlgItem(hDlg, IDC_MIC_MONO_MIX), micEnabled);
EnableWindow(GetDlgItem(hDlg, IDC_MICROPHONE_LABEL), micEnabled);
EnableWindow(GetDlgItem(hDlg, IDC_MICROPHONE), micEnabled);
// Handle webcam size combo change — disable shape when Full screen selected
if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_WEBCAM_SIZE) {
bool isGif = (g_RecordingFormat == RecordingFormat::GIF);
bool webcamEnabled = !isGif && IsDlgButtonChecked(hDlg, IDC_WEBCAM_OVERLAY) == BST_CHECKED;
bool isFullScreen = SendMessage(GetDlgItem(hDlg, IDC_WEBCAM_SIZE), CB_GETCURSEL, 0, 0) == 4;
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_SHAPE_LABEL), webcamEnabled && !isFullScreen);
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_SHAPE), webcamEnabled && !isFullScreen);
}
switch ( LOWORD( wParam )) {
case IDC_WEBCAM_SETTINGS:
DialogBox( g_hInstance, L"WEBCAM_SETTINGS", hDlg, WebcamSettingsProc );
break;
case IDC_ADVANCED_BREAK:
DialogBox( g_hInstance, L"ADVANCED_BREAK", hDlg, AdvancedBreakProc );
break;
@@ -4874,6 +4577,7 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message,
DWORD newDrawToggleKey, newDrawToggleMod, newBreakToggleMod, newDemoTypeToggleMod, newRecordToggleMod, newSnipToggleMod, newSnipPanoramaToggleMod, newSnipOcrToggleMod;
DWORD newLiveZoomToggleKey, newLiveZoomToggleMod;
static std::vector<std::pair<std::wstring, std::wstring>> microphones;
static std::vector<std::pair<std::wstring, std::wstring>> webcams;
auto CleanupFonts = [&]()
{
@@ -5145,9 +4849,6 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message,
CheckDlgButton( g_OptionsTabs[RECORD_PAGE].hPage, IDC_MIC_MONO_MIX,
g_MicMonoMix ? BST_CHECKED: BST_UNCHECKED );
CheckDlgButton( g_OptionsTabs[RECORD_PAGE].hPage, IDC_NOISE_CANCELLATION,
g_NoiseCancellation ? BST_CHECKED: BST_UNCHECKED );
CheckDlgButton( g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_ASPECT_RATIO,
g_RecordAspectRatio ? BST_CHECKED: BST_UNCHECKED );
@@ -5223,19 +4924,101 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message,
bool isGifSelected = (g_RecordingFormat == RecordingFormat::GIF);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_SYSTEM_AUDIO), !isGifSelected);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_AUDIO), !isGifSelected);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_NOISE_CANCELLATION), !isGifSelected && g_CaptureAudio);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_MIC_MONO_MIX), !isGifSelected && g_CaptureAudio);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE_LABEL), !isGifSelected && g_CaptureAudio);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE), !isGifSelected && g_CaptureAudio);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE_LABEL), !isGifSelected);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE), !isGifSelected);
// Webcam overlay controls
CheckDlgButton( g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_OVERLAY,
g_WebcamOverlay ? BST_CHECKED : BST_UNCHECKED );
// Set initial enabled state for webcam overlay and settings button
// Enumerate webcam devices
webcams.clear();
{
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_OVERLAY), !isGifSelected);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_SETTINGS), !isGifSelected && g_WebcamOverlay);
MFStartup( MF_VERSION, MFSTARTUP_LITE );
IMFAttributes* pAttributes = nullptr;
if( SUCCEEDED( MFCreateAttributes( &pAttributes, 1 ) ) )
{
pAttributes->SetGUID( MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID );
IMFActivate** ppDevices = nullptr;
UINT32 count = 0;
if( SUCCEEDED( MFEnumDeviceSources( pAttributes, &ppDevices, &count ) ) )
{
for( UINT32 i = 0; i < count; i++ )
{
WCHAR* symLink = nullptr;
UINT32 symLinkLen = 0;
WCHAR* friendlyName = nullptr;
UINT32 nameLen = 0;
if( SUCCEEDED( ppDevices[i]->GetAllocatedString( MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &symLink, &symLinkLen ) ) &&
SUCCEEDED( ppDevices[i]->GetAllocatedString( MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &friendlyName, &nameLen ) ) )
{
webcams.emplace_back( symLink, friendlyName );
}
if( symLink ) CoTaskMemFree( symLink );
if( friendlyName ) CoTaskMemFree( friendlyName );
ppDevices[i]->Release();
}
CoTaskMemFree( ppDevices );
}
pAttributes->Release();
}
MFShutdown();
}
// Add webcam devices to combo box
SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_DEVICE ), static_cast<UINT>(CB_ADDSTRING), static_cast<WPARAM>(0), reinterpret_cast<LPARAM>(L"Default") );
selection = 0;
for( size_t i = 0; i < webcams.size(); i++ )
{
SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_DEVICE ), static_cast<UINT>(CB_ADDSTRING), static_cast<WPARAM>(0), reinterpret_cast<LPARAM>(webcams[i].second.c_str()) );
if( selection == 0 && wcscmp( webcams[i].first.c_str(), g_WebcamDeviceSymLink ) == 0 )
{
selection = i + 1;
}
}
SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_DEVICE ), CB_SETCURSEL, static_cast<WPARAM>(selection), static_cast<LPARAM>(0) );
// Webcam position combo
{
const wchar_t* positions[] = { L"Top left", L"Top right", L"Bottom left", L"Bottom right" };
for( int i = 0; i < 4; i++ )
SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_POSITION ), static_cast<UINT>(CB_ADDSTRING), static_cast<WPARAM>(0), reinterpret_cast<LPARAM>(positions[i]) );
SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_POSITION ), CB_SETCURSEL, static_cast<WPARAM>(g_WebcamPosition), static_cast<LPARAM>(0) );
}
// Webcam size combo
{
const wchar_t* sizes[] = { L"Small", L"Medium", L"Large", L"X-Large", L"Full screen" };
for( int i = 0; i < 5; i++ )
SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_SIZE ), static_cast<UINT>(CB_ADDSTRING), static_cast<WPARAM>(0), reinterpret_cast<LPARAM>(sizes[i]) );
SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_SIZE ), CB_SETCURSEL, static_cast<WPARAM>(g_WebcamSize), static_cast<LPARAM>(0) );
}
// Webcam shape combo
{
const wchar_t* shapes[] = { L"Rectangle", L"Rounded rectangle", L"Rounded square", L"Circle" };
for( int i = 0; i < 4; i++ )
SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_SHAPE ), static_cast<UINT>(CB_ADDSTRING), static_cast<WPARAM>(0), reinterpret_cast<LPARAM>(shapes[i]) );
SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_SHAPE ), CB_SETCURSEL, static_cast<WPARAM>(g_WebcamShape), static_cast<LPARAM>(0) );
}
// Set initial enabled state for webcam controls.
// Disable everything if no webcam is detected on the system.
{
bool hasWebcam = !webcams.empty();
bool webcamEnabled = hasWebcam && !isGifSelected && g_WebcamOverlay;
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_OVERLAY), hasWebcam && !isGifSelected);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_DEVICE_LABEL), webcamEnabled);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_DEVICE), webcamEnabled);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_POSITION_LABEL), webcamEnabled);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_POSITION), webcamEnabled);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_SIZE_LABEL), webcamEnabled);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_SIZE), webcamEnabled);
{
bool isFullScreen = webcamEnabled && g_WebcamSize == 4;
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_SHAPE_LABEL), webcamEnabled && !isFullScreen);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_SHAPE), webcamEnabled && !isFullScreen);
}
}
if( GetFileAttributes( g_DemoTypeFile ) == -1 )
@@ -5589,7 +5372,6 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message,
g_CaptureSystemAudio = IsDlgButtonChecked(g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_SYSTEM_AUDIO) == BST_CHECKED;
g_CaptureAudio = IsDlgButtonChecked(g_OptionsTabs[RECORD_PAGE].hPage, IDC_CAPTURE_AUDIO) == BST_CHECKED;
g_MicMonoMix = IsDlgButtonChecked(g_OptionsTabs[RECORD_PAGE].hPage, IDC_MIC_MONO_MIX) == BST_CHECKED;
g_NoiseCancellation = IsDlgButtonChecked(g_OptionsTabs[RECORD_PAGE].hPage, IDC_NOISE_CANCELLATION) == BST_CHECKED;
g_RecordAspectRatio = IsDlgButtonChecked(g_OptionsTabs[RECORD_PAGE].hPage, IDC_RECORD_ASPECT_RATIO) == BST_CHECKED;
GetDlgItemText( g_OptionsTabs[BREAK_PAGE].hPage, IDC_TIMER, text, 3 );
text[2] = 0;
@@ -5603,8 +5385,15 @@ INT_PTR CALLBACK OptionsProc( HWND hDlg, UINT message,
int index = static_cast<int>(SendMessage( GetDlgItem( g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE ), static_cast<UINT>(CB_GETCURSEL), static_cast<WPARAM>(0), static_cast<LPARAM>(0) ));
_tcscpy( g_MicrophoneDeviceId, index == 0 ? L"" : microphones[static_cast<size_t>(index) - 1].first.c_str() );
// Get the webcam overlay setting (other webcam settings are saved in WebcamSettingsProc)
// Get the webcam settings
g_WebcamOverlay = IsDlgButtonChecked(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_OVERLAY) == BST_CHECKED;
g_WebcamPosition = static_cast<DWORD>(SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_POSITION), CB_GETCURSEL, 0, 0));
g_WebcamSize = static_cast<DWORD>(SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_SIZE), CB_GETCURSEL, 0, 0));
g_WebcamShape = static_cast<DWORD>(SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_SHAPE), CB_GETCURSEL, 0, 0));
{
int wcIndex = static_cast<int>(SendMessage(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_DEVICE), CB_GETCURSEL, 0, 0));
_tcscpy( g_WebcamDeviceSymLink, wcIndex == 0 ? L"" : webcams[static_cast<size_t>(wcIndex) - 1].first.c_str() );
}
if( newToggleKey && !RegisterHotKey( GetParent( hDlg ), ZOOM_HOTKEY, newToggleMod, newToggleKey & 0xFF )) {
@@ -6914,22 +6703,6 @@ winrt::fire_and_forget StartRecordingAsync( HWND hWnd, LPRECT rcCrop, HWND hWndR
// Capture the UI thread context so we can resume on it for the save dialog
winrt::apartment_context uiThread;
// Start audio initialization as early as possible. AudioGraph creation
// and microphone device opening take ~1400 ms. By starting here, the
// init runs in the background during D3D device creation, capture-item
// creation, file I/O, and the entire VideoRecordingSession constructor
// (webcam probe + warmup), giving it ~1400 ms of overlap — enough to
// finish before StartAsync even needs the result.
std::unique_ptr<AudioSampleGenerator> audioGenerator;
winrt::Windows::Foundation::IAsyncAction audioInitAction{ nullptr };
if ((g_RecordingFormat != RecordingFormat::GIF) && (g_CaptureAudio || g_CaptureSystemAudio))
{
audioGenerator = std::make_unique<AudioSampleGenerator>(
g_CaptureAudio, g_CaptureSystemAudio, g_MicMonoMix, g_NoiseCancellation );
audioInitAction = audioGenerator->InitializeAsync();
_diagLog( L"audio InitializeAsync started (background)" );
}
auto tempFolderPath = std::filesystem::temp_directory_path().wstring();
auto tempFolder = co_await winrt::StorageFolder::GetFolderFromPathAsync( tempFolderPath );
auto appFolder = co_await tempFolder.CreateFolderAsync( L"ZoomIt", winrt::CreationCollisionOption::OpenIfExists );
@@ -7018,8 +6791,9 @@ winrt::fire_and_forget StartRecordingAsync( HWND hWnd, LPRECT rcCrop, HWND hWndR
item,
*rcCrop,
g_RecordFrameRate,
std::move(audioGenerator),
audioInitAction,
g_CaptureAudio,
g_CaptureSystemAudio,
g_MicMonoMix,
stream );
_diagLog( L"VideoRecordingSession::Create returned" );

View File

@@ -25,7 +25,4 @@ RCZOOMITSCR BINRES MOVEABLE PURE "ZoomItBreak64.scr"
RCZOOMITSCR BINRES MOVEABLE PURE "ZoomItBreak64a.scr"
#endif
// Embed the selfie segmentation ONNX model (platform-independent).
RCSEGMODEL BINRES MOVEABLE PURE "selfie_segmentation.onnx"
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "ZoomIt.exe.manifest"

View File

@@ -127,16 +127,6 @@
#define IDC_WEBCAM_SHAPE_LABEL 1121
#define IDC_TRIM_APPEND 1122
#define IDC_RECORD_ASPECT_RATIO 1123
#define IDC_WEBCAM_BACKGROUND_BLUR 1124
#define IDC_WEBCAM_BG_LABEL 1125
#define IDC_WEBCAM_BG_MODE 1126
#define IDC_WEBCAM_BG_IMAGE 1127
#define IDC_WEBCAM_BG_BROWSE 1128
#define IDC_THIRDPARTY_NOTICES 1129
#define IDC_WEBCAM_SETTINGS 1130
#define IDC_WEBCAM_BRIGHTNESS_LABEL 1131
#define IDC_WEBCAM_BRIGHTNESS_SLIDER 1132
#define IDC_NOISE_CANCELLATION 1133
#define IDC_SAVE 40002
#define IDC_COPY 40004
#define IDC_RECORD 40006
@@ -152,7 +142,7 @@
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 120
#define _APS_NEXT_COMMAND_VALUE 40015
#define _APS_NEXT_CONTROL_VALUE 1134
#define _APS_NEXT_CONTROL_VALUE 1124
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@@ -1,32 +0,0 @@
Copyright (c) 2007-2017, 2024 Jean-Marc Valin
Copyright (c) 2023 Amazon
Copyright (c) 2017, Mozilla
Copyright (c) 2005-2017, Xiph.Org Foundation
Copyright (c) 2003-2004, Mark Borgerding
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.Org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,182 +0,0 @@
/*Copyright (c) 2003-2004, Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.*/
#ifndef KISS_FFT_GUTS_H
#define KISS_FFT_GUTS_H
#define MIN(a,b) ((a)<(b) ? (a):(b))
#define MAX(a,b) ((a)>(b) ? (a):(b))
/* kiss_fft.h
defines kiss_fft_scalar as either short or a float type
and defines
typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */
#include "kiss_fft.h"
/*
Explanation of macros dealing with complex math:
C_MUL(m,a,b) : m = a*b
C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise
C_SUB( res, a,b) : res = a - b
C_SUBFROM( res , a) : res -= a
C_ADDTO( res , a) : res += a
* */
#ifdef FIXED_POINT
#include "arch.h"
#define SAMP_MAX 2147483647
#define TWID_MAX 32767
#define TRIG_UPSCALE 1
#define SAMP_MIN -SAMP_MAX
# define S_MUL(a,b) MULT16_32_Q15(b, a)
# define C_MUL(m,a,b) \
do{ (m).r = SUB32_ovflw(S_MUL((a).r,(b).r) , S_MUL((a).i,(b).i)); \
(m).i = ADD32_ovflw(S_MUL((a).r,(b).i) , S_MUL((a).i,(b).r)); }while(0)
# define C_MULC(m,a,b) \
do{ (m).r = ADD32_ovflw(S_MUL((a).r,(b).r) , S_MUL((a).i,(b).i)); \
(m).i = SUB32_ovflw(S_MUL((a).i,(b).r) , S_MUL((a).r,(b).i)); }while(0)
# define C_MULBYSCALAR( c, s ) \
do{ (c).r = S_MUL( (c).r , s ) ;\
(c).i = S_MUL( (c).i , s ) ; }while(0)
# define DIVSCALAR(x,k) \
(x) = S_MUL( x, (TWID_MAX-((k)>>1))/(k)+1 )
# define C_FIXDIV(c,div) \
do { DIVSCALAR( (c).r , div); \
DIVSCALAR( (c).i , div); }while (0)
#define C_ADD( res, a,b)\
do {(res).r=ADD32_ovflw((a).r,(b).r); (res).i=ADD32_ovflw((a).i,(b).i); \
}while(0)
#define C_SUB( res, a,b)\
do {(res).r=SUB32_ovflw((a).r,(b).r); (res).i=SUB32_ovflw((a).i,(b).i); \
}while(0)
#define C_ADDTO( res , a)\
do {(res).r = ADD32_ovflw((res).r, (a).r); (res).i = ADD32_ovflw((res).i,(a).i);\
}while(0)
#define C_SUBFROM( res , a)\
do {(res).r = ADD32_ovflw((res).r,(a).r); (res).i = SUB32_ovflw((res).i,(a).i); \
}while(0)
#if defined(OPUS_ARM_INLINE_ASM)
#include "arm/kiss_fft_armv4.h"
#endif
#if defined(OPUS_ARM_INLINE_EDSP)
#include "arm/kiss_fft_armv5e.h"
#endif
#if defined(MIPSr1_ASM)
#include "mips/kiss_fft_mipsr1.h"
#endif
#else /* not FIXED_POINT*/
# define S_MUL(a,b) ( (a)*(b) )
#define C_MUL(m,a,b) \
do{ (m).r = (a).r*(b).r - (a).i*(b).i;\
(m).i = (a).r*(b).i + (a).i*(b).r; }while(0)
#define C_MULC(m,a,b) \
do{ (m).r = (a).r*(b).r + (a).i*(b).i;\
(m).i = (a).i*(b).r - (a).r*(b).i; }while(0)
#define C_MUL4(m,a,b) C_MUL(m,a,b)
# define C_FIXDIV(c,div) /* NOOP */
# define C_MULBYSCALAR( c, s ) \
do{ (c).r *= (s);\
(c).i *= (s); }while(0)
#endif
#ifndef CHECK_OVERFLOW_OP
# define CHECK_OVERFLOW_OP(a,op,b) /* noop */
#endif
#ifndef C_ADD
#define C_ADD( res, a,b)\
do { \
CHECK_OVERFLOW_OP((a).r,+,(b).r)\
CHECK_OVERFLOW_OP((a).i,+,(b).i)\
(res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \
}while(0)
#define C_SUB( res, a,b)\
do { \
CHECK_OVERFLOW_OP((a).r,-,(b).r)\
CHECK_OVERFLOW_OP((a).i,-,(b).i)\
(res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \
}while(0)
#define C_ADDTO( res , a)\
do { \
CHECK_OVERFLOW_OP((res).r,+,(a).r)\
CHECK_OVERFLOW_OP((res).i,+,(a).i)\
(res).r += (a).r; (res).i += (a).i;\
}while(0)
#define C_SUBFROM( res , a)\
do {\
CHECK_OVERFLOW_OP((res).r,-,(a).r)\
CHECK_OVERFLOW_OP((res).i,-,(a).i)\
(res).r -= (a).r; (res).i -= (a).i; \
}while(0)
#endif /* C_ADD defined */
#ifdef FIXED_POINT
/*# define KISS_FFT_COS(phase) TRIG_UPSCALE*floor(MIN(32767,MAX(-32767,.5+32768 * cos (phase))))
# define KISS_FFT_SIN(phase) TRIG_UPSCALE*floor(MIN(32767,MAX(-32767,.5+32768 * sin (phase))))*/
# define KISS_FFT_COS(phase) floor(.5+TWID_MAX*cos (phase))
# define KISS_FFT_SIN(phase) floor(.5+TWID_MAX*sin (phase))
# define HALF_OF(x) ((x)>>1)
#elif defined(USE_SIMD)
# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
# define HALF_OF(x) ((x)*_mm_set1_ps(.5f))
#else
# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
# define HALF_OF(x) ((x)*.5f)
#endif
#define kf_cexp(x,phase) \
do{ \
(x)->r = KISS_FFT_COS(phase);\
(x)->i = KISS_FFT_SIN(phase);\
}while(0)
#define kf_cexp2(x,phase) \
do{ \
(x)->r = TRIG_UPSCALE*celt_cos_norm((phase));\
(x)->i = TRIG_UPSCALE*celt_cos_norm((phase)-32768);\
}while(0)
#endif /* KISS_FFT_GUTS_H */

View File

@@ -1,261 +0,0 @@
/* Copyright (c) 2003-2008 Jean-Marc Valin
Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2009 Xiph.Org Foundation
Written by Jean-Marc Valin */
/**
@file arch.h
@brief Various architecture definitions for CELT
*/
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ARCH_H
#define ARCH_H
#include "opus_types.h"
#include "common.h"
# if !defined(__GNUC_PREREQ)
# if defined(__GNUC__)&&defined(__GNUC_MINOR__)
# define __GNUC_PREREQ(_maj,_min) \
((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min))
# else
# define __GNUC_PREREQ(_maj,_min) 0
# endif
# endif
#define CELT_SIG_SCALE 32768.f
#define celt_fatal(str) _celt_fatal(str, __FILE__, __LINE__);
#ifdef ENABLE_ASSERTIONS
#include <stdio.h>
#include <stdlib.h>
#ifdef __GNUC__
__attribute__((noreturn))
#endif
static OPUS_INLINE void _celt_fatal(const char *str, const char *file, int line)
{
fprintf (stderr, "Fatal (internal) error in %s, line %d: %s\n", file, line, str);
abort();
}
#define celt_assert(cond) {if (!(cond)) {celt_fatal("assertion failed: " #cond);}}
#define celt_assert2(cond, message) {if (!(cond)) {celt_fatal("assertion failed: " #cond "\n" message);}}
#else
#define celt_assert(cond)
#define celt_assert2(cond, message)
#endif
#define IMUL32(a,b) ((a)*(b))
#define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum 16-bit value. */
#define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */
#define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum 32-bit value. */
#define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */
#define IMIN(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum int value. */
#define IMAX(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum int value. */
#define UADD32(a,b) ((a)+(b))
#define USUB32(a,b) ((a)-(b))
/* Set this if opus_int64 is a native type of the CPU. */
/* Assume that all LP64 architectures have fast 64-bit types; also x86_64
(which can be ILP32 for x32) and Win64 (which is LLP64). */
#if defined(__x86_64__) || defined(__LP64__) || defined(_WIN64)
#define OPUS_FAST_INT64 1
#else
#define OPUS_FAST_INT64 0
#endif
#define PRINT_MIPS(file)
#ifdef FIXED_POINT
typedef opus_int16 opus_val16;
typedef opus_int32 opus_val32;
typedef opus_int64 opus_val64;
typedef opus_val32 celt_sig;
typedef opus_val16 celt_norm;
typedef opus_val32 celt_ener;
#define Q15ONE 32767
#define SIG_SHIFT 12
/* Safe saturation value for 32-bit signals. Should be less than
2^31*(1-0.85) to avoid blowing up on DC at deemphasis.*/
#define SIG_SAT (300000000)
#define NORM_SCALING 16384
#define DB_SHIFT 10
#define EPSILON 1
#define VERY_SMALL 0
#define VERY_LARGE16 ((opus_val16)32767)
#define Q15_ONE ((opus_val16)32767)
#define SCALEIN(a) (a)
#define SCALEOUT(a) (a)
#define ABS16(x) ((x) < 0 ? (-(x)) : (x))
#define ABS32(x) ((x) < 0 ? (-(x)) : (x))
static OPUS_INLINE opus_int16 SAT16(opus_int32 x) {
return x > 32767 ? 32767 : x < -32768 ? -32768 : (opus_int16)x;
}
#ifdef FIXED_DEBUG
#include "fixed_debug.h"
#else
#include "fixed_generic.h"
#ifdef OPUS_ARM_PRESUME_AARCH64_NEON_INTR
#include "arm/fixed_arm64.h"
#elif OPUS_ARM_INLINE_EDSP
#include "arm/fixed_armv5e.h"
#elif defined (OPUS_ARM_INLINE_ASM)
#include "arm/fixed_armv4.h"
#elif defined (BFIN_ASM)
#include "fixed_bfin.h"
#elif defined (TI_C5X_ASM)
#include "fixed_c5x.h"
#elif defined (TI_C6X_ASM)
#include "fixed_c6x.h"
#endif
#endif
#else /* FIXED_POINT */
typedef float opus_val16;
typedef float opus_val32;
typedef float opus_val64;
typedef float celt_sig;
typedef float celt_norm;
typedef float celt_ener;
#ifdef FLOAT_APPROX
/* This code should reliably detect NaN/inf even when -ffast-math is used.
Assumes IEEE 754 format. */
static OPUS_INLINE int celt_isnan(float x)
{
union {float f; opus_uint32 i;} in;
in.f = x;
return ((in.i>>23)&0xFF)==0xFF && (in.i&0x007FFFFF)!=0;
}
#else
#ifdef __FAST_MATH__
#error Cannot build libopus with -ffast-math unless FLOAT_APPROX is defined. This could result in crashes on extreme (e.g. NaN) input
#endif
#define celt_isnan(x) ((x)!=(x))
#endif
#define Q15ONE 1.0f
#define NORM_SCALING 1.f
#define EPSILON 1e-15f
#define VERY_SMALL 1e-30f
#define VERY_LARGE16 1e15f
#define Q15_ONE ((opus_val16)1.f)
/* This appears to be the same speed as C99's fabsf() but it's more portable. */
#define ABS16(x) ((float)fabs(x))
#define ABS32(x) ((float)fabs(x))
#define QCONST16(x,bits) (x)
#define QCONST32(x,bits) (x)
#define NEG16(x) (-(x))
#define NEG32(x) (-(x))
#define NEG32_ovflw(x) (-(x))
#define EXTRACT16(x) (x)
#define EXTEND32(x) (x)
#define SHR16(a,shift) (a)
#define SHL16(a,shift) (a)
#define SHR32(a,shift) (a)
#define SHL32(a,shift) (a)
#define PSHR32(a,shift) (a)
#define VSHR32(a,shift) (a)
#define PSHR(a,shift) (a)
#define SHR(a,shift) (a)
#define SHL(a,shift) (a)
#define SATURATE(x,a) (x)
#define SATURATE16(x) (x)
#define ROUND16(a,shift) (a)
#define SROUND16(a,shift) (a)
#define HALF16(x) (.5f*(x))
#define HALF32(x) (.5f*(x))
#define ADD16(a,b) ((a)+(b))
#define SUB16(a,b) ((a)-(b))
#define ADD32(a,b) ((a)+(b))
#define SUB32(a,b) ((a)-(b))
#define ADD32_ovflw(a,b) ((a)+(b))
#define SUB32_ovflw(a,b) ((a)-(b))
#define MULT16_16_16(a,b) ((a)*(b))
#define MULT16_16(a,b) ((opus_val32)(a)*(opus_val32)(b))
#define MAC16_16(c,a,b) ((c)+(opus_val32)(a)*(opus_val32)(b))
#define MULT16_32_Q15(a,b) ((a)*(b))
#define MULT16_32_Q16(a,b) ((a)*(b))
#define MULT32_32_Q31(a,b) ((a)*(b))
#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b))
#define MAC16_32_Q16(c,a,b) ((c)+(a)*(b))
#define MULT16_16_Q11_32(a,b) ((a)*(b))
#define MULT16_16_Q11(a,b) ((a)*(b))
#define MULT16_16_Q13(a,b) ((a)*(b))
#define MULT16_16_Q14(a,b) ((a)*(b))
#define MULT16_16_Q15(a,b) ((a)*(b))
#define MULT16_16_P15(a,b) ((a)*(b))
#define MULT16_16_P13(a,b) ((a)*(b))
#define MULT16_16_P14(a,b) ((a)*(b))
#define MULT16_32_P16(a,b) ((a)*(b))
#define DIV32_16(a,b) (((opus_val32)(a))/(opus_val16)(b))
#define DIV32(a,b) (((opus_val32)(a))/(opus_val32)(b))
#define SCALEIN(a) ((a)*CELT_SIG_SCALE)
#define SCALEOUT(a) ((a)*(1/CELT_SIG_SCALE))
#define SIG2WORD16(x) (x)
#endif /* !FIXED_POINT */
#ifndef GLOBAL_STACK_SIZE
#ifdef FIXED_POINT
#define GLOBAL_STACK_SIZE 120000
#else
#define GLOBAL_STACK_SIZE 120000
#endif
#endif
#endif /* ARCH_H */

View File

@@ -1,174 +0,0 @@
/* Copyright (c) 2009-2010 Xiph.Org Foundation
Written by Jean-Marc Valin */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "celt_lpc.h"
#include "arch.h"
#include "common.h"
#include "pitch.h"
#include "denoise.h"
void rnn_lpc(
opus_val16 *_lpc, /* out: [0...p-1] LPC coefficients */
const opus_val32 *ac, /* in: [0...p] autocorrelation values */
int p
)
{
int i, j;
opus_val32 r;
opus_val32 error = ac[0];
#ifdef FIXED_POINT
opus_val32 lpc[LPC_ORDER];
#else
float *lpc = _lpc;
#endif
RNN_CLEAR(lpc, p);
if (ac[0] != 0)
{
for (i = 0; i < p; i++) {
/* Sum up this iteration's reflection coefficient */
opus_val32 rr = 0;
for (j = 0; j < i; j++)
rr += MULT32_32_Q31(lpc[j],ac[i - j]);
rr += SHR32(ac[i + 1],3);
r = -SHL32(rr,3)/error;
/* Update LPC coefficients and total error */
lpc[i] = SHR32(r,3);
for (j = 0; j < (i+1)>>1; j++)
{
opus_val32 tmp1, tmp2;
tmp1 = lpc[j];
tmp2 = lpc[i-1-j];
lpc[j] = tmp1 + MULT32_32_Q31(r,tmp2);
lpc[i-1-j] = tmp2 + MULT32_32_Q31(r,tmp1);
}
error = error - MULT32_32_Q31(MULT32_32_Q31(r,r),error);
/* Bail out once we get 30 dB gain */
#ifdef FIXED_POINT
if (error<SHR32(ac[0],10))
break;
#else
if (error<.001f*ac[0])
break;
#endif
}
}
#ifdef FIXED_POINT
for (i=0;i<p;i++)
_lpc[i] = ROUND16(lpc[i],16);
#endif
}
int rnn_autocorr(
const opus_val16 *x, /* in: [0...n-1] samples x */
opus_val32 *ac, /* out: [0...lag-1] ac values */
const opus_val16 *window,
int overlap,
int lag,
int n)
{
opus_val32 d;
int i, k;
int fastN=n-lag;
int shift;
const opus_val16 *xptr;
opus_val16 xx[PITCH_BUF_SIZE/2];
celt_assert(n>0);
celt_assert(n<=PITCH_BUF_SIZE/2)
celt_assert(overlap>=0);
if (overlap == 0)
{
xptr = x;
} else {
for (i=0;i<n;i++)
xx[i] = x[i];
for (i=0;i<overlap;i++)
{
xx[i] = MULT16_16_Q15(x[i],window[i]);
xx[n-i-1] = MULT16_16_Q15(x[n-i-1],window[i]);
}
xptr = xx;
}
shift=0;
#ifdef FIXED_POINT
{
opus_val32 ac0;
ac0 = 1+(n<<7);
if (n&1) ac0 += SHR32(MULT16_16(xptr[0],xptr[0]),9);
for(i=(n&1);i<n;i+=2)
{
ac0 += SHR32(MULT16_16(xptr[i],xptr[i]),9);
ac0 += SHR32(MULT16_16(xptr[i+1],xptr[i+1]),9);
}
shift = celt_ilog2(ac0)-30+10;
shift = (shift)/2;
if (shift>0)
{
for(i=0;i<n;i++)
xx[i] = PSHR32(xptr[i], shift);
xptr = xx;
} else
shift = 0;
}
#endif
rnn_pitch_xcorr(xptr, xptr, ac, fastN, lag+1);
for (k=0;k<=lag;k++)
{
for (i = k+fastN, d = 0; i < n; i++)
d = MAC16_16(d, xptr[i], xptr[i-k]);
ac[k] += d;
}
#ifdef FIXED_POINT
shift = 2*shift;
if (shift<=0)
ac[0] += SHL32((opus_int32)1, -shift);
if (ac[0] < 268435456)
{
int shift2 = 29 - EC_ILOG(ac[0]);
for (i=0;i<=lag;i++)
ac[i] = SHL32(ac[i], shift2);
shift -= shift2;
} else if (ac[0] >= 536870912)
{
int shift2=1;
if (ac[0] >= 1073741824)
shift2++;
for (i=0;i<=lag;i++)
ac[i] = SHR32(ac[i], shift2);
shift += shift2;
}
#endif
return shift;
}

View File

@@ -1,45 +0,0 @@
/* Copyright (c) 2009-2010 Xiph.Org Foundation
Written by Jean-Marc Valin */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PLC_H
#define PLC_H
#include "arch.h"
#include "common.h"
#if defined(OPUS_X86_MAY_HAVE_SSE4_1)
#include "x86/celt_lpc_sse.h"
#endif
#define LPC_ORDER 24
void rnn_lpc(opus_val16 *_lpc, const opus_val32 *ac, int p);
int rnn_autocorr(const opus_val16 *x, opus_val32 *ac,
const opus_val16 *window, int overlap, int lag, int n);
#endif /* PLC_H */

View File

@@ -1,56 +0,0 @@
#ifndef COMMON_H
#define COMMON_H
#include "stdlib.h"
#include "string.h"
#define RNN_INLINE inline
#define OPUS_INLINE inline
/** RNNoise wrapper for malloc(). To do your own dynamic allocation, all you need t
o do is replace this function and rnnoise_free */
#ifndef OVERRIDE_RNNOISE_ALLOC
static RNN_INLINE void *rnnoise_alloc (size_t size)
{
return malloc(size);
}
#endif
/** RNNoise wrapper for free(). To do your own dynamic allocation, all you need to do is replace this function and rnnoise_alloc */
#ifndef OVERRIDE_RNNOISE_FREE
static RNN_INLINE void rnnoise_free (void *ptr)
{
free(ptr);
}
#endif
/** Copy n elements from src to dst. The 0* term provides compile-time type checking */
#ifndef OVERRIDE_RNN_COPY
#define RNN_COPY(dst, src, n) (memcpy((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) ))
#endif
/** Copy n elements from src to dst, allowing overlapping regions. The 0* term
provides compile-time type checking */
#ifndef OVERRIDE_RNN_MOVE
#define RNN_MOVE(dst, src, n) (memmove((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) ))
#endif
/** Set n elements of dst to zero */
#ifndef OVERRIDE_RNN_CLEAR
#define RNN_CLEAR(dst, n) (memset((dst), 0, (n)*sizeof(*(dst))))
#endif
# if !defined(OPUS_GNUC_PREREQ)
# if defined(__GNUC__)&&defined(__GNUC_MINOR__)
# define OPUS_GNUC_PREREQ(_maj,_min) \
((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min))
# else
# define OPUS_GNUC_PREREQ(_maj,_min) 0
# endif
# endif
#endif

View File

@@ -1,53 +0,0 @@
/* Copyright (c) 2010 Xiph.Org Foundation
* Copyright (c) 2013 Parrot */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CPU_SUPPORT_H
#define CPU_SUPPORT_H
#include "opus_types.h"
#include "common.h"
#ifdef RNN_ENABLE_X86_RTCD
#include "x86/x86cpu.h"
/* We currently support 5 x86 variants:
* arch[0] -> sse2
* arch[1] -> sse4.1
* arch[2] -> avx2
*/
#define OPUS_ARCHMASK 3
int rnn_select_arch(void);
#else
#define OPUS_ARCHMASK 0
static OPUS_INLINE int rnn_select_arch(void)
{
return 0;
}
#endif
#endif

View File

@@ -1,505 +0,0 @@
/* Copyright (c) 2024 Jean-Marc Valin
* Copyright (c) 2018 Gregor Richards
* Copyright (c) 2017 Mozilla */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "kiss_fft.h"
#include "common.h"
#include "denoise.h"
#include <math.h>
#include "rnnoise.h"
#include "pitch.h"
#include "arch.h"
#include "rnn.h"
#include "cpu_support.h"
#define SQUARE(x) ((x)*(x))
#ifndef TRAINING
#define TRAINING 0
#endif
/* ERB bandwidths going in reverse from 20 kHz and then replacing the 700 and 800
with just 750 because having 32 bands is convenient for the DNN.
B(1)=400;
for k=2:35
B(k) = B(k-1) - max(2, round(24.7*(4.37*B(k-1)/20+1)/50));
end
printf("%d, ", B(end:-1:1));
printf("\n")
*/
const int eband20ms[NB_BANDS+2] = {
/*0 100 200 300 400 500 600 750 900 1.1 1.2 1.4 1.6 1.8 2.1 2.4 2.7 3.0 3.4 3.9 4.4 4.9 5.5 6.2 7.0 7.9 8.8 9.9 11.2 12.6 14.1 15.9 17.8 20.0*/
0, 2, 4, 6, 8, 10, 12, 15, 18, 21, 24, 28, 32, 36, 41, 47, 53, 60, 68, 77, 87, 98, 110, 124, 140, 157, 176, 198, 223, 251, 282, 317, 356, 400};
struct DenoiseState {
RNNoise model;
#if !TRAINING
int arch;
#endif
float analysis_mem[FRAME_SIZE];
int memid;
float synthesis_mem[FRAME_SIZE];
float pitch_buf[PITCH_BUF_SIZE];
float pitch_enh_buf[PITCH_BUF_SIZE];
float last_gain;
int last_period;
float mem_hp_x[2];
float lastg[NB_BANDS];
RNNState rnn;
kiss_fft_cpx delayed_X[FREQ_SIZE];
kiss_fft_cpx delayed_P[FREQ_SIZE];
float delayed_Ex[NB_BANDS], delayed_Ep[NB_BANDS];
float delayed_Exp[NB_BANDS];
};
static void compute_band_energy(float *bandE, const kiss_fft_cpx *X) {
int i;
float sum[NB_BANDS+2] = {0};
for (i=0;i<NB_BANDS+1;i++)
{
int j;
int band_size;
band_size = eband20ms[i+1]-eband20ms[i];
for (j=0;j<band_size;j++) {
float tmp;
float frac = (float)j/band_size;
tmp = SQUARE(X[eband20ms[i] + j].r);
tmp += SQUARE(X[eband20ms[i] + j].i);
sum[i] += (1-frac)*tmp;
sum[i+1] += frac*tmp;
}
}
sum[1] = (sum[0]+sum[1])*2/3;
sum[NB_BANDS] = (sum[NB_BANDS]+sum[NB_BANDS+1])*2/3;
for (i=0;i<NB_BANDS;i++)
{
bandE[i] = sum[i+1];
}
}
static void compute_band_corr(float *bandE, const kiss_fft_cpx *X, const kiss_fft_cpx *P) {
int i;
float sum[NB_BANDS+2] = {0};
for (i=0;i<NB_BANDS+1;i++)
{
int j;
int band_size;
band_size = eband20ms[i+1]-eband20ms[i];
for (j=0;j<band_size;j++) {
float tmp;
float frac = (float)j/band_size;
tmp = X[eband20ms[i] + j].r * P[eband20ms[i] + j].r;
tmp += X[eband20ms[i] + j].i * P[eband20ms[i] + j].i;
sum[i] += (1-frac)*tmp;
sum[i+1] += frac*tmp;
}
}
sum[1] = (sum[0]+sum[1])*2/3;
sum[NB_BANDS] = (sum[NB_BANDS]+sum[NB_BANDS+1])*2/3;
for (i=0;i<NB_BANDS;i++)
{
bandE[i] = sum[i+1];
}
}
static void interp_band_gain(float *g, const float *bandE) {
int i,j;
memset(g, 0, FREQ_SIZE);
for (i=1;i<NB_BANDS;i++)
{
int band_size;
band_size = eband20ms[i+1]-eband20ms[i];
for (j=0;j<band_size;j++) {
float frac = (float)j/band_size;
g[eband20ms[i] + j] = (1-frac)*bandE[i-1] + frac*bandE[i];
}
}
for (j=0;j<eband20ms[1];j++) g[j] = bandE[0];
for (j=eband20ms[NB_BANDS];j<eband20ms[NB_BANDS+1];j++) g[j] = bandE[NB_BANDS-1];
}
extern const float rnn_dct_table[];
extern const kiss_fft_state rnn_kfft;
extern const float rnn_half_window[];
static void dct(float *out, const float *in) {
int i;
for (i=0;i<NB_BANDS;i++) {
int j;
float sum = 0;
for (j=0;j<NB_BANDS;j++) {
sum += in[j] * rnn_dct_table[j*NB_BANDS + i];
}
out[i] = sum*sqrt(2./22);
}
}
#if 0
static void idct(float *out, const float *in) {
int i;
for (i=0;i<NB_BANDS;i++) {
int j;
float sum = 0;
for (j=0;j<NB_BANDS;j++) {
sum += in[j] * rnn_dct_table[i*NB_BANDS + j];
}
out[i] = sum*sqrt(2./22);
}
}
#endif
static void forward_transform(kiss_fft_cpx *out, const float *in) {
int i;
kiss_fft_cpx x[WINDOW_SIZE];
kiss_fft_cpx y[WINDOW_SIZE];
for (i=0;i<WINDOW_SIZE;i++) {
x[i].r = in[i];
x[i].i = 0;
}
rnn_fft(&rnn_kfft, x, y, 0);
for (i=0;i<FREQ_SIZE;i++) {
out[i] = y[i];
}
}
static void inverse_transform(float *out, const kiss_fft_cpx *in) {
int i;
kiss_fft_cpx x[WINDOW_SIZE];
kiss_fft_cpx y[WINDOW_SIZE];
for (i=0;i<FREQ_SIZE;i++) {
x[i] = in[i];
}
for (;i<WINDOW_SIZE;i++) {
x[i].r = x[WINDOW_SIZE - i].r;
x[i].i = -x[WINDOW_SIZE - i].i;
}
rnn_fft(&rnn_kfft, x, y, 0);
/* output in reverse order for IFFT. */
out[0] = WINDOW_SIZE*y[0].r;
for (i=1;i<WINDOW_SIZE;i++) {
out[i] = WINDOW_SIZE*y[WINDOW_SIZE - i].r;
}
}
static void apply_window(float *x) {
int i;
for (i=0;i<FRAME_SIZE;i++) {
x[i] *= rnn_half_window[i];
x[WINDOW_SIZE - 1 - i] *= rnn_half_window[i];
}
}
struct RNNModel {
/* Set either blob or const_blob. */
const void *const_blob;
void *blob;
int blob_len;
FILE *file;
};
RNNModel *rnnoise_model_from_buffer(const void *ptr, int len) {
RNNModel *model;
model = malloc(sizeof(*model));
model->blob = NULL;
model->const_blob = ptr;
model->blob_len = len;
return model;
}
RNNModel *rnnoise_model_from_filename(const char *filename) {
RNNModel *model;
FILE *f = fopen(filename, "rb");
model = rnnoise_model_from_file(f);
model->file = f;
return model;
}
RNNModel *rnnoise_model_from_file(FILE *f) {
RNNModel *model;
model = malloc(sizeof(*model));
model->file = NULL;
fseek(f, 0, SEEK_END);
model->blob_len = ftell(f);
fseek(f, 0, SEEK_SET);
model->const_blob = NULL;
model->blob = malloc(model->blob_len);
if (fread(model->blob, model->blob_len, 1, f) != 1)
{
rnnoise_model_free(model);
return NULL;
}
return model;
}
void rnnoise_model_free(RNNModel *model) {
if (model->file != NULL) fclose(model->file);
if (model->blob != NULL) free(model->blob);
free(model);
}
int rnnoise_get_size(void) {
return sizeof(DenoiseState);
}
int rnnoise_get_frame_size(void) {
return FRAME_SIZE;
}
int rnnoise_init(DenoiseState *st, RNNModel *model) {
memset(st, 0, sizeof(*st));
#if !TRAINING
if (model != NULL) {
WeightArray *list;
int ret = 1;
parse_weights(&list, model->blob ? model->blob : model->const_blob, model->blob_len);
if (list != NULL) {
ret = init_rnnoise(&st->model, list);
opus_free(list);
}
if (ret != 0) return -1;
}
#ifndef USE_WEIGHTS_FILE
else {
int ret = init_rnnoise(&st->model, rnnoise_arrays);
if (ret != 0) return -1;
}
#endif
st->arch = rnn_select_arch();
#else
(void)model;
#endif
return 0;
}
DenoiseState *rnnoise_create(RNNModel *model) {
int ret;
DenoiseState *st;
st = malloc(rnnoise_get_size());
ret = rnnoise_init(st, model);
if (ret != 0) {
free(st);
return NULL;
}
return st;
}
void rnnoise_destroy(DenoiseState *st) {
free(st);
}
#if TRAINING
extern int lowpass;
extern int band_lp;
#endif
void rnn_frame_analysis(DenoiseState *st, kiss_fft_cpx *X, float *Ex, const float *in) {
int i;
float x[WINDOW_SIZE];
RNN_COPY(x, st->analysis_mem, FRAME_SIZE);
for (i=0;i<FRAME_SIZE;i++) x[FRAME_SIZE + i] = in[i];
RNN_COPY(st->analysis_mem, in, FRAME_SIZE);
apply_window(x);
forward_transform(X, x);
#if TRAINING
for (i=lowpass;i<FREQ_SIZE;i++)
X[i].r = X[i].i = 0;
#endif
compute_band_energy(Ex, X);
}
int rnn_compute_frame_features(DenoiseState *st, kiss_fft_cpx *X, kiss_fft_cpx *P,
float *Ex, float *Ep, float *Exp, float *features, const float *in) {
int i;
float E = 0;
float Ly[NB_BANDS];
float p[WINDOW_SIZE];
float pitch_buf[PITCH_BUF_SIZE>>1];
int pitch_index;
float gain;
float *(pre[1]);
float follow, logMax;
rnn_frame_analysis(st, X, Ex, in);
RNN_MOVE(st->pitch_buf, &st->pitch_buf[FRAME_SIZE], PITCH_BUF_SIZE-FRAME_SIZE);
RNN_COPY(&st->pitch_buf[PITCH_BUF_SIZE-FRAME_SIZE], in, FRAME_SIZE);
pre[0] = &st->pitch_buf[0];
rnn_pitch_downsample(pre, pitch_buf, PITCH_BUF_SIZE, 1);
rnn_pitch_search(pitch_buf+(PITCH_MAX_PERIOD>>1), pitch_buf, PITCH_FRAME_SIZE,
PITCH_MAX_PERIOD-3*PITCH_MIN_PERIOD, &pitch_index);
pitch_index = PITCH_MAX_PERIOD-pitch_index;
gain = rnn_remove_doubling(pitch_buf, PITCH_MAX_PERIOD, PITCH_MIN_PERIOD,
PITCH_FRAME_SIZE, &pitch_index, st->last_period, st->last_gain);
st->last_period = pitch_index;
st->last_gain = gain;
for (i=0;i<WINDOW_SIZE;i++)
p[i] = st->pitch_buf[PITCH_BUF_SIZE-WINDOW_SIZE-pitch_index+i];
apply_window(p);
forward_transform(P, p);
compute_band_energy(Ep, P);
compute_band_corr(Exp, X, P);
for (i=0;i<NB_BANDS;i++) Exp[i] = Exp[i]/sqrt(.001+Ex[i]*Ep[i]);
dct(&features[NB_BANDS], Exp);
features[2*NB_BANDS] = .01*(pitch_index-300);
logMax = -2;
follow = -2;
for (i=0;i<NB_BANDS;i++) {
Ly[i] = log10(1e-2+Ex[i]);
Ly[i] = MAX16(logMax-7, MAX16(follow-1.5, Ly[i]));
logMax = MAX16(logMax, Ly[i]);
follow = MAX16(follow-1.5, Ly[i]);
E += Ex[i];
}
if (!TRAINING && E < 0.04) {
/* If there's no audio, avoid messing up the state. */
RNN_CLEAR(features, NB_FEATURES);
return 1;
}
dct(features, Ly);
features[0] -= 12;
features[1] -= 4;
return TRAINING && E < 0.1;
}
static void frame_synthesis(DenoiseState *st, float *out, const kiss_fft_cpx *y) {
float x[WINDOW_SIZE];
int i;
inverse_transform(x, y);
apply_window(x);
for (i=0;i<FRAME_SIZE;i++) out[i] = x[i] + st->synthesis_mem[i];
RNN_COPY(st->synthesis_mem, &x[FRAME_SIZE], FRAME_SIZE);
}
void rnn_biquad(float *y, float mem[2], const float *x, const float *b, const float *a, int N) {
int i;
for (i=0;i<N;i++) {
float xi, yi;
xi = x[i];
yi = x[i] + mem[0];
mem[0] = mem[1] + (b[0]*(double)xi - a[0]*(double)yi);
mem[1] = (b[1]*(double)xi - a[1]*(double)yi);
y[i] = yi;
}
}
void rnn_pitch_filter(kiss_fft_cpx *X, const kiss_fft_cpx *P, const float *Ex, const float *Ep,
const float *Exp, const float *g) {
int i;
float r[NB_BANDS];
float rf[FREQ_SIZE] = {0};
float newE[NB_BANDS];
float norm[NB_BANDS];
float normf[FREQ_SIZE]={0};
for (i=0;i<NB_BANDS;i++) {
#if 0
if (Exp[i]>g[i]) r[i] = 1;
else r[i] = Exp[i]*(1-g[i])/(.001 + g[i]*(1-Exp[i]));
r[i] = MIN16(1, MAX16(0, r[i]));
#else
if (Exp[i]>g[i]) r[i] = 1;
else r[i] = SQUARE(Exp[i])*(1-SQUARE(g[i]))/(.001 + SQUARE(g[i])*(1-SQUARE(Exp[i])));
r[i] = sqrt(MIN16(1, MAX16(0, r[i])));
#endif
r[i] *= sqrt(Ex[i]/(1e-8+Ep[i]));
}
interp_band_gain(rf, r);
for (i=0;i<FREQ_SIZE;i++) {
X[i].r += rf[i]*P[i].r;
X[i].i += rf[i]*P[i].i;
}
compute_band_energy(newE, X);
for (i=0;i<NB_BANDS;i++) {
norm[i] = sqrt(Ex[i]/(1e-8+newE[i]));
}
interp_band_gain(normf, norm);
for (i=0;i<FREQ_SIZE;i++) {
X[i].r *= normf[i];
X[i].i *= normf[i];
}
}
float rnnoise_process_frame(DenoiseState *st, float *out, const float *in) {
int i;
kiss_fft_cpx X[FREQ_SIZE];
kiss_fft_cpx P[FREQ_SIZE];
float x[FRAME_SIZE];
float Ex[NB_BANDS], Ep[NB_BANDS];
float Exp[NB_BANDS];
float features[NB_FEATURES];
float g[NB_BANDS];
float gf[FREQ_SIZE]={1};
float vad_prob = 0;
int silence;
static const float a_hp[2] = {-1.99599, 0.99600};
static const float b_hp[2] = {-2, 1};
rnn_biquad(x, st->mem_hp_x, in, b_hp, a_hp, FRAME_SIZE);
silence = rnn_compute_frame_features(st, X, P, Ex, Ep, Exp, features, x);
if (!silence) {
#if !TRAINING
compute_rnn(&st->model, &st->rnn, g, &vad_prob, features, st->arch);
#endif
rnn_pitch_filter(st->delayed_X, st->delayed_P, st->delayed_Ex, st->delayed_Ep, st->delayed_Exp, g);
for (i=0;i<NB_BANDS;i++) {
float alpha = .6f;
/* Cap the decay at 0.6 per frame, corresponding to an RT60 of 135 ms.
That avoids unnaturally quick attenuation. */
g[i] = MAX16(g[i], alpha*st->lastg[i]);
/* Compensate for energy change across frame when computing the threshold gain.
Avoids leaking noise when energy increases (e.g. transient noise). */
st->lastg[i] = MIN16(1.f, g[i]*(st->delayed_Ex[i]+1e-3)/(Ex[i]+1e-3));
}
interp_band_gain(gf, g);
#if 1
for (i=0;i<FREQ_SIZE;i++) {
st->delayed_X[i].r *= gf[i];
st->delayed_X[i].i *= gf[i];
}
#endif
}
frame_synthesis(st, out, st->delayed_X);
RNN_COPY(st->delayed_X, X, FREQ_SIZE);
RNN_COPY(st->delayed_P, P, FREQ_SIZE);
RNN_COPY(st->delayed_Ex, Ex, NB_BANDS);
RNN_COPY(st->delayed_Ep, Ep, NB_BANDS);
RNN_COPY(st->delayed_Exp, Exp, NB_BANDS);
return vad_prob;
}

View File

@@ -1,56 +0,0 @@
/* Copyright (c) 2017 Mozilla */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "rnnoise.h"
#include "kiss_fft.h"
#include "nnet.h"
#define FRAME_SIZE 480
#define WINDOW_SIZE (2*FRAME_SIZE)
#define FREQ_SIZE (FRAME_SIZE + 1)
#define NB_BANDS 32
#define NB_FEATURES (2*NB_BANDS+1)
#define PITCH_MIN_PERIOD 60
#define PITCH_MAX_PERIOD 768
#define PITCH_FRAME_SIZE 960
#define PITCH_BUF_SIZE (PITCH_MAX_PERIOD+PITCH_FRAME_SIZE)
extern const WeightArray rnnoise_arrays[];
extern const int eband20ms[];
void rnn_biquad(float *y, float mem[2], const float *x, const float *b, const float *a, int N);
void rnn_pitch_filter(kiss_fft_cpx *X, const kiss_fft_cpx *P, const float *Ex, const float *Ep,
const float *Exp, const float *g);
void rnn_frame_analysis(DenoiseState *st, kiss_fft_cpx *X, float *Ex, const float *in);
int rnn_compute_frame_features(DenoiseState *st, kiss_fft_cpx *X, kiss_fft_cpx *P,
float *Ex, float *Ep, float *Exp, float *features, const float *in);

View File

@@ -1,601 +0,0 @@
/*Copyright (c) 2003-2004, Mark Borgerding
Lots of modifications by Jean-Marc Valin
Copyright (c) 2005-2007, Xiph.Org Foundation
Copyright (c) 2008, Xiph.Org Foundation, CSIRO
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.*/
/* This code is originally from Mark Borgerding's KISS-FFT but has been
heavily modified to better suit Opus */
#ifndef SKIP_CONFIG_H
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
#endif
#include "_kiss_fft_guts.h"
#define CUSTOM_MODES
/* The guts header contains all the multiplication and addition macros that are defined for
complex numbers. It also declares the kf_ internal functions.
*/
static void kf_bfly2(
kiss_fft_cpx * Fout,
int m,
int N
)
{
kiss_fft_cpx * Fout2;
int i;
(void)m;
#ifdef CUSTOM_MODES
if (m==1)
{
celt_assert(m==1);
for (i=0;i<N;i++)
{
kiss_fft_cpx t;
Fout2 = Fout + 1;
t = *Fout2;
C_SUB( *Fout2 , *Fout , t );
C_ADDTO( *Fout , t );
Fout += 2;
}
} else
#endif
{
opus_val16 tw;
tw = QCONST16(0.7071067812f, 15);
/* We know that m==4 here because the radix-2 is just after a radix-4 */
celt_assert(m==4);
for (i=0;i<N;i++)
{
kiss_fft_cpx t;
Fout2 = Fout + 4;
t = Fout2[0];
C_SUB( Fout2[0] , Fout[0] , t );
C_ADDTO( Fout[0] , t );
t.r = S_MUL(ADD32_ovflw(Fout2[1].r, Fout2[1].i), tw);
t.i = S_MUL(SUB32_ovflw(Fout2[1].i, Fout2[1].r), tw);
C_SUB( Fout2[1] , Fout[1] , t );
C_ADDTO( Fout[1] , t );
t.r = Fout2[2].i;
t.i = -Fout2[2].r;
C_SUB( Fout2[2] , Fout[2] , t );
C_ADDTO( Fout[2] , t );
t.r = S_MUL(SUB32_ovflw(Fout2[3].i, Fout2[3].r), tw);
t.i = S_MUL(NEG32_ovflw(ADD32_ovflw(Fout2[3].i, Fout2[3].r)), tw);
C_SUB( Fout2[3] , Fout[3] , t );
C_ADDTO( Fout[3] , t );
Fout += 8;
}
}
}
static void kf_bfly4(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_state *st,
int m,
int N,
int mm
)
{
int i;
if (m==1)
{
/* Degenerate case where all the twiddles are 1. */
for (i=0;i<N;i++)
{
kiss_fft_cpx scratch0, scratch1;
C_SUB( scratch0 , *Fout, Fout[2] );
C_ADDTO(*Fout, Fout[2]);
C_ADD( scratch1 , Fout[1] , Fout[3] );
C_SUB( Fout[2], *Fout, scratch1 );
C_ADDTO( *Fout , scratch1 );
C_SUB( scratch1 , Fout[1] , Fout[3] );
Fout[1].r = ADD32_ovflw(scratch0.r, scratch1.i);
Fout[1].i = SUB32_ovflw(scratch0.i, scratch1.r);
Fout[3].r = SUB32_ovflw(scratch0.r, scratch1.i);
Fout[3].i = ADD32_ovflw(scratch0.i, scratch1.r);
Fout+=4;
}
} else {
int j;
kiss_fft_cpx scratch[6];
const kiss_twiddle_cpx *tw1,*tw2,*tw3;
const int m2=2*m;
const int m3=3*m;
kiss_fft_cpx * Fout_beg = Fout;
for (i=0;i<N;i++)
{
Fout = Fout_beg + i*mm;
tw3 = tw2 = tw1 = st->twiddles;
/* m is guaranteed to be a multiple of 4. */
for (j=0;j<m;j++)
{
C_MUL(scratch[0],Fout[m] , *tw1 );
C_MUL(scratch[1],Fout[m2] , *tw2 );
C_MUL(scratch[2],Fout[m3] , *tw3 );
C_SUB( scratch[5] , *Fout, scratch[1] );
C_ADDTO(*Fout, scratch[1]);
C_ADD( scratch[3] , scratch[0] , scratch[2] );
C_SUB( scratch[4] , scratch[0] , scratch[2] );
C_SUB( Fout[m2], *Fout, scratch[3] );
tw1 += fstride;
tw2 += fstride*2;
tw3 += fstride*3;
C_ADDTO( *Fout , scratch[3] );
Fout[m].r = ADD32_ovflw(scratch[5].r, scratch[4].i);
Fout[m].i = SUB32_ovflw(scratch[5].i, scratch[4].r);
Fout[m3].r = SUB32_ovflw(scratch[5].r, scratch[4].i);
Fout[m3].i = ADD32_ovflw(scratch[5].i, scratch[4].r);
++Fout;
}
}
}
}
#ifndef RADIX_TWO_ONLY
static void kf_bfly3(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_state *st,
int m,
int N,
int mm
)
{
int i;
size_t k;
const size_t m2 = 2*m;
const kiss_twiddle_cpx *tw1,*tw2;
kiss_fft_cpx scratch[5];
kiss_twiddle_cpx epi3;
kiss_fft_cpx * Fout_beg = Fout;
#ifdef FIXED_POINT
/*epi3.r = -16384;*/ /* Unused */
epi3.i = -28378;
#else
epi3 = st->twiddles[fstride*m];
#endif
for (i=0;i<N;i++)
{
Fout = Fout_beg + i*mm;
tw1=tw2=st->twiddles;
/* For non-custom modes, m is guaranteed to be a multiple of 4. */
k=m;
do {
C_MUL(scratch[1],Fout[m] , *tw1);
C_MUL(scratch[2],Fout[m2] , *tw2);
C_ADD(scratch[3],scratch[1],scratch[2]);
C_SUB(scratch[0],scratch[1],scratch[2]);
tw1 += fstride;
tw2 += fstride*2;
Fout[m].r = SUB32_ovflw(Fout->r, HALF_OF(scratch[3].r));
Fout[m].i = SUB32_ovflw(Fout->i, HALF_OF(scratch[3].i));
C_MULBYSCALAR( scratch[0] , epi3.i );
C_ADDTO(*Fout,scratch[3]);
Fout[m2].r = ADD32_ovflw(Fout[m].r, scratch[0].i);
Fout[m2].i = SUB32_ovflw(Fout[m].i, scratch[0].r);
Fout[m].r = SUB32_ovflw(Fout[m].r, scratch[0].i);
Fout[m].i = ADD32_ovflw(Fout[m].i, scratch[0].r);
++Fout;
} while(--k);
}
}
#ifndef OVERRIDE_kf_bfly5
static void kf_bfly5(
kiss_fft_cpx * Fout,
const size_t fstride,
const kiss_fft_state *st,
int m,
int N,
int mm
)
{
kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
int i, u;
kiss_fft_cpx scratch[13];
const kiss_twiddle_cpx *tw;
kiss_twiddle_cpx ya,yb;
kiss_fft_cpx * Fout_beg = Fout;
#ifdef FIXED_POINT
ya.r = 10126;
ya.i = -31164;
yb.r = -26510;
yb.i = -19261;
#else
ya = st->twiddles[fstride*m];
yb = st->twiddles[fstride*2*m];
#endif
tw=st->twiddles;
for (i=0;i<N;i++)
{
Fout = Fout_beg + i*mm;
Fout0=Fout;
Fout1=Fout0+m;
Fout2=Fout0+2*m;
Fout3=Fout0+3*m;
Fout4=Fout0+4*m;
/* For non-custom modes, m is guaranteed to be a multiple of 4. */
for ( u=0; u<m; ++u ) {
scratch[0] = *Fout0;
C_MUL(scratch[1] ,*Fout1, tw[u*fstride]);
C_MUL(scratch[2] ,*Fout2, tw[2*u*fstride]);
C_MUL(scratch[3] ,*Fout3, tw[3*u*fstride]);
C_MUL(scratch[4] ,*Fout4, tw[4*u*fstride]);
C_ADD( scratch[7],scratch[1],scratch[4]);
C_SUB( scratch[10],scratch[1],scratch[4]);
C_ADD( scratch[8],scratch[2],scratch[3]);
C_SUB( scratch[9],scratch[2],scratch[3]);
Fout0->r = ADD32_ovflw(Fout0->r, ADD32_ovflw(scratch[7].r, scratch[8].r));
Fout0->i = ADD32_ovflw(Fout0->i, ADD32_ovflw(scratch[7].i, scratch[8].i));
scratch[5].r = ADD32_ovflw(scratch[0].r, ADD32_ovflw(S_MUL(scratch[7].r,ya.r), S_MUL(scratch[8].r,yb.r)));
scratch[5].i = ADD32_ovflw(scratch[0].i, ADD32_ovflw(S_MUL(scratch[7].i,ya.r), S_MUL(scratch[8].i,yb.r)));
scratch[6].r = ADD32_ovflw(S_MUL(scratch[10].i,ya.i), S_MUL(scratch[9].i,yb.i));
scratch[6].i = NEG32_ovflw(ADD32_ovflw(S_MUL(scratch[10].r,ya.i), S_MUL(scratch[9].r,yb.i)));
C_SUB(*Fout1,scratch[5],scratch[6]);
C_ADD(*Fout4,scratch[5],scratch[6]);
scratch[11].r = ADD32_ovflw(scratch[0].r, ADD32_ovflw(S_MUL(scratch[7].r,yb.r), S_MUL(scratch[8].r,ya.r)));
scratch[11].i = ADD32_ovflw(scratch[0].i, ADD32_ovflw(S_MUL(scratch[7].i,yb.r), S_MUL(scratch[8].i,ya.r)));
scratch[12].r = SUB32_ovflw(S_MUL(scratch[9].i,ya.i), S_MUL(scratch[10].i,yb.i));
scratch[12].i = SUB32_ovflw(S_MUL(scratch[10].r,yb.i), S_MUL(scratch[9].r,ya.i));
C_ADD(*Fout2,scratch[11],scratch[12]);
C_SUB(*Fout3,scratch[11],scratch[12]);
++Fout0;++Fout1;++Fout2;++Fout3;++Fout4;
}
}
}
#endif /* OVERRIDE_kf_bfly5 */
#endif
#ifdef CUSTOM_MODES
static
void compute_bitrev_table(
int Fout,
opus_int32 *f,
const size_t fstride,
int in_stride,
opus_int16 * factors,
const kiss_fft_state *st
)
{
const int p=*factors++; /* the radix */
const int m=*factors++; /* stage's fft length/p */
/*printf ("fft %d %d %d %d %d %d\n", p*m, m, p, s2, fstride*in_stride, N);*/
if (m==1)
{
int j;
for (j=0;j<p;j++)
{
*f = Fout+j;
f += fstride*in_stride;
}
} else {
int j;
for (j=0;j<p;j++)
{
compute_bitrev_table( Fout , f, fstride*p, in_stride, factors,st);
f += fstride*in_stride;
Fout += m;
}
}
}
/* facbuf is populated by p1,m1,p2,m2, ...
where
p[i] * m[i] = m[i-1]
m0 = n */
static
int kf_factor(int n,opus_int16 * facbuf)
{
int p=4;
int i;
int stages=0;
int nbak = n;
/*factor out powers of 4, powers of 2, then any remaining primes */
do {
while (n % p) {
switch (p) {
case 4: p = 2; break;
case 2: p = 3; break;
default: p += 2; break;
}
if (p>32000 || (opus_int32)p*(opus_int32)p > n)
p = n; /* no more factors, skip to end */
}
n /= p;
#ifdef RADIX_TWO_ONLY
if (p!=2 && p != 4)
#else
if (p>5)
#endif
{
return 0;
}
facbuf[2*stages] = p;
if (p==2 && stages > 1)
{
facbuf[2*stages] = 4;
facbuf[2] = 2;
}
stages++;
} while (n > 1);
n = nbak;
/* Reverse the order to get the radix 4 at the end, so we can use the
fast degenerate case. It turns out that reversing the order also
improves the noise behaviour. */
for (i=0;i<stages/2;i++)
{
int tmp;
tmp = facbuf[2*i];
facbuf[2*i] = facbuf[2*(stages-i-1)];
facbuf[2*(stages-i-1)] = tmp;
}
for (i=0;i<stages;i++)
{
n /= facbuf[2*i];
facbuf[2*i+1] = n;
}
return 1;
}
static void compute_twiddles(kiss_twiddle_cpx *twiddles, int nfft)
{
int i;
#ifdef FIXED_POINT
for (i=0;i<nfft;++i) {
opus_val32 phase = -i;
kf_cexp2(twiddles+i, DIV32(SHL32(phase,17),nfft));
}
#else
for (i=0;i<nfft;++i) {
const double pi=3.14159265358979323846264338327;
double phase = ( -2*pi /nfft ) * i;
kf_cexp(twiddles+i, phase );
}
#endif
}
int rnn_fft_alloc_arch_c(kiss_fft_state *st) {
(void)st;
return 0;
}
/*
*
* Allocates all necessary storage space for the fft and ifft.
* The return value is a contiguous block of memory. As such,
* It can be freed with free().
* */
kiss_fft_state *rnn_fft_alloc_twiddles(int nfft,void * mem,size_t * lenmem,
const kiss_fft_state *base, int arch)
{
kiss_fft_state *st=NULL;
size_t memneeded = sizeof(struct kiss_fft_state); /* twiddle factors*/
if ( lenmem==NULL ) {
st = ( kiss_fft_state*)KISS_FFT_MALLOC( memneeded );
}else{
if (mem != NULL && *lenmem >= memneeded)
st = (kiss_fft_state*)mem;
*lenmem = memneeded;
}
if (st) {
opus_int32 *bitrev;
kiss_twiddle_cpx *twiddles;
st->nfft=nfft;
#ifdef FIXED_POINT
st->scale_shift = celt_ilog2(st->nfft);
if (st->nfft == 1<<st->scale_shift)
st->scale = Q15ONE;
else
st->scale = (1073741824+st->nfft/2)/st->nfft>>(15-st->scale_shift);
#else
st->scale = 1.f/nfft;
#endif
if (base != NULL)
{
st->twiddles = base->twiddles;
st->shift = 0;
while (st->shift < 32 && nfft<<st->shift != base->nfft)
st->shift++;
if (st->shift>=32)
goto fail;
} else {
st->twiddles = twiddles = (kiss_twiddle_cpx*)KISS_FFT_MALLOC(sizeof(kiss_twiddle_cpx)*nfft);
compute_twiddles(twiddles, nfft);
st->shift = -1;
}
if (!kf_factor(nfft,st->factors))
{
goto fail;
}
/* bitrev */
st->bitrev = bitrev = (opus_int32*)KISS_FFT_MALLOC(sizeof(opus_int32)*nfft);
if (st->bitrev==NULL)
goto fail;
compute_bitrev_table(0, bitrev, 1,1, st->factors,st);
/* Initialize architecture specific fft parameters */
if (rnn_fft_alloc_arch(st, arch))
goto fail;
}
return st;
fail:
rnn_fft_free(st, arch);
return NULL;
}
kiss_fft_state *rnn_fft_alloc(int nfft,void * mem,size_t * lenmem, int arch)
{
return rnn_fft_alloc_twiddles(nfft, mem, lenmem, NULL, arch);
}
void rnn_fft_free_arch_c(kiss_fft_state *st) {
(void)st;
}
void rnn_fft_free(const kiss_fft_state *cfg, int arch)
{
if (cfg)
{
rnn_fft_free_arch((kiss_fft_state *)cfg, arch);
opus_free((opus_int32*)cfg->bitrev);
if (cfg->shift < 0)
opus_free((kiss_twiddle_cpx*)cfg->twiddles);
opus_free((kiss_fft_state*)cfg);
}
}
#endif /* CUSTOM_MODES */
void rnn_fft_impl(const kiss_fft_state *st,kiss_fft_cpx *fout)
{
int m2, m;
int p;
int L;
int fstride[MAXFACTORS];
int i;
int shift;
/* st->shift can be -1 */
shift = st->shift>0 ? st->shift : 0;
fstride[0] = 1;
L=0;
do {
p = st->factors[2*L];
m = st->factors[2*L+1];
fstride[L+1] = fstride[L]*p;
L++;
} while(m!=1);
m = st->factors[2*L-1];
for (i=L-1;i>=0;i--)
{
if (i!=0)
m2 = st->factors[2*i-1];
else
m2 = 1;
switch (st->factors[2*i])
{
case 2:
kf_bfly2(fout, m, fstride[i]);
break;
case 4:
kf_bfly4(fout,fstride[i]<<shift,st,m, fstride[i], m2);
break;
#ifndef RADIX_TWO_ONLY
case 3:
kf_bfly3(fout,fstride[i]<<shift,st,m, fstride[i], m2);
break;
case 5:
kf_bfly5(fout,fstride[i]<<shift,st,m, fstride[i], m2);
break;
#endif
}
m = m2;
}
}
void rnn_fft_c(const kiss_fft_state *st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout)
{
int i;
opus_val16 scale;
#ifdef FIXED_POINT
/* Allows us to scale with MULT16_32_Q16(), which is faster than
MULT16_32_Q15() on ARM. */
int scale_shift = st->scale_shift-1;
#endif
scale = st->scale;
celt_assert2 (fin != fout, "In-place FFT not supported");
/* Bit-reverse the input */
for (i=0;i<st->nfft;i++)
{
kiss_fft_cpx x = fin[i];
fout[st->bitrev[i]].r = SHR32(MULT16_32_Q16(scale, x.r), scale_shift);
fout[st->bitrev[i]].i = SHR32(MULT16_32_Q16(scale, x.i), scale_shift);
}
rnn_fft_impl(st, fout);
}
void rnn_ifft_c(const kiss_fft_state *st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout)
{
int i;
celt_assert2 (fin != fout, "In-place FFT not supported");
/* Bit-reverse the input */
for (i=0;i<st->nfft;i++)
fout[st->bitrev[i]] = fin[i];
for (i=0;i<st->nfft;i++)
fout[i].i = -fout[i].i;
rnn_fft_impl(st, fout);
for (i=0;i<st->nfft;i++)
fout[i].i = -fout[i].i;
}

View File

@@ -1,203 +0,0 @@
/*Copyright (c) 2003-2004, Mark Borgerding
Lots of modifications by Jean-Marc Valin
Copyright (c) 2005-2007, Xiph.Org Foundation
Copyright (c) 2008, Xiph.Org Foundation, CSIRO
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.*/
#ifndef KISS_FFT_H
#define KISS_FFT_H
#include <stdlib.h>
#include <math.h>
#include "arch.h"
#include <stdlib.h>
#define opus_alloc(x) malloc(x)
#define opus_free(x) free(x)
#ifdef __cplusplus
extern "C" {
#endif
#ifdef USE_SIMD
# include <xmmintrin.h>
# define kiss_fft_scalar __m128
#define KISS_FFT_MALLOC(nbytes) memalign(16,nbytes)
#else
#define KISS_FFT_MALLOC opus_alloc
#endif
#ifdef FIXED_POINT
#include "arch.h"
# define kiss_fft_scalar opus_int32
# define kiss_twiddle_scalar opus_int16
#else
# ifndef kiss_fft_scalar
/* default is float */
# define kiss_fft_scalar float
# define kiss_twiddle_scalar float
# define KF_SUFFIX _celt_single
# endif
#endif
typedef struct {
kiss_fft_scalar r;
kiss_fft_scalar i;
}kiss_fft_cpx;
typedef struct {
kiss_twiddle_scalar r;
kiss_twiddle_scalar i;
}kiss_twiddle_cpx;
#define MAXFACTORS 8
/* e.g. an fft of length 128 has 4 factors
as far as kissfft is concerned
4*4*4*2
*/
typedef struct arch_fft_state{
int is_supported;
void *priv;
} arch_fft_state;
typedef struct kiss_fft_state{
int nfft;
opus_val16 scale;
#ifdef FIXED_POINT
int scale_shift;
#endif
int shift;
opus_int16 factors[2*MAXFACTORS];
const opus_int32 *bitrev;
const kiss_twiddle_cpx *twiddles;
arch_fft_state *arch_fft;
} kiss_fft_state;
#if defined(HAVE_ARM_NE10)
#include "arm/fft_arm.h"
#endif
/*typedef struct kiss_fft_state* kiss_fft_cfg;*/
/**
* opus_fft_alloc
*
* Initialize a FFT (or IFFT) algorithm's cfg/state buffer.
*
* typical usage: kiss_fft_cfg mycfg=opus_fft_alloc(1024,0,NULL,NULL);
*
* The return value from fft_alloc is a cfg buffer used internally
* by the fft routine or NULL.
*
* If lenmem is NULL, then opus_fft_alloc will allocate a cfg buffer using malloc.
* The returned value should be free()d when done to avoid memory leaks.
*
* The state can be placed in a user supplied buffer 'mem':
* If lenmem is not NULL and mem is not NULL and *lenmem is large enough,
* then the function places the cfg in mem and the size used in *lenmem
* and returns mem.
*
* If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough),
* then the function returns NULL and places the minimum cfg
* buffer size in *lenmem.
* */
kiss_fft_state *rnn_fft_alloc_twiddles(int nfft,void * mem,size_t * lenmem, const kiss_fft_state *base, int arch);
kiss_fft_state *rnn_fft_alloc(int nfft,void * mem,size_t * lenmem, int arch);
/**
* opus_fft(cfg,in_out_buf)
*
* Perform an FFT on a complex input buffer.
* for a forward FFT,
* fin should be f[0] , f[1] , ... ,f[nfft-1]
* fout will be F[0] , F[1] , ... ,F[nfft-1]
* Note that each element is complex and can be accessed like
f[k].r and f[k].i
* */
void rnn_fft_c(const kiss_fft_state *cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
void rnn_ifft_c(const kiss_fft_state *cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
void rnn_fft_impl(const kiss_fft_state *st,kiss_fft_cpx *fout);
void rnn_ifft_impl(const kiss_fft_state *st,kiss_fft_cpx *fout);
void rnn_fft_free(const kiss_fft_state *cfg, int arch);
void rnn_fft_free_arch_c(kiss_fft_state *st);
int rnn_fft_alloc_arch_c(kiss_fft_state *st);
#if !defined(OVERRIDE_OPUS_FFT)
/* Is run-time CPU detection enabled on this platform? */
#if defined(OPUS_HAVE_RTCD) && (defined(HAVE_ARM_NE10))
extern int (*const OPUS_FFT_ALLOC_ARCH_IMPL[OPUS_ARCHMASK+1])(
kiss_fft_state *st);
#define opus_fft_alloc_arch(_st, arch) \
((*OPUS_FFT_ALLOC_ARCH_IMPL[(arch)&OPUS_ARCHMASK])(_st))
extern void (*const OPUS_FFT_FREE_ARCH_IMPL[OPUS_ARCHMASK+1])(
kiss_fft_state *st);
#define opus_fft_free_arch(_st, arch) \
((*OPUS_FFT_FREE_ARCH_IMPL[(arch)&OPUS_ARCHMASK])(_st))
extern void (*const OPUS_FFT[OPUS_ARCHMASK+1])(const kiss_fft_state *cfg,
const kiss_fft_cpx *fin, kiss_fft_cpx *fout);
#define opus_fft(_cfg, _fin, _fout, arch) \
((*OPUS_FFT[(arch)&OPUS_ARCHMASK])(_cfg, _fin, _fout))
extern void (*const OPUS_IFFT[OPUS_ARCHMASK+1])(const kiss_fft_state *cfg,
const kiss_fft_cpx *fin, kiss_fft_cpx *fout);
#define opus_ifft(_cfg, _fin, _fout, arch) \
((*OPUS_IFFT[(arch)&OPUS_ARCHMASK])(_cfg, _fin, _fout))
#else /* else for if defined(OPUS_HAVE_RTCD) && (defined(HAVE_ARM_NE10)) */
#define rnn_fft_alloc_arch(_st, arch) \
((void)(arch), rnn_fft_alloc_arch_c(_st))
#define rnn_fft_free_arch(_st, arch) \
((void)(arch), rnn_fft_free_arch_c(_st))
#define rnn_fft(_cfg, _fin, _fout, arch) \
((void)(arch), rnn_fft_c(_cfg, _fin, _fout))
#define rnn_ifft(_cfg, _fin, _fout, arch) \
((void)(arch), rnn_ifft_c(_cfg, _fin, _fout))
#endif /* end if defined(OPUS_HAVE_RTCD) && (defined(HAVE_ARM_NE10)) */
#endif /* end if !defined(OVERRIDE_OPUS_FFT) */
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,123 +0,0 @@
/* Copyright (c) 2018 Mozilla
2008-2011 Octasic Inc.
2012-2017 Jean-Marc Valin */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <math.h>
#include "opus_types.h"
#include "arch.h"
#include "nnet.h"
#include "common.h"
#include "vec.h"
#ifdef ENABLE_OSCE
#include "osce.h"
#endif
#ifdef NO_OPTIMIZATIONS
#if defined(_MSC_VER)
#pragma message ("Compiling without any vectorization. This code will be very slow")
#else
#warning Compiling without any vectorization. This code will be very slow
#endif
#endif
#define SOFTMAX_HACK
void compute_generic_dense(const LinearLayer *layer, float *output, const float *input, int activation, int arch)
{
compute_linear(layer, output, input, arch);
compute_activation(output, output, layer->nb_outputs, activation, arch);
}
#define MAX_RNN_NEURONS_ALL 1024
void compute_generic_gru(const LinearLayer *input_weights, const LinearLayer *recurrent_weights, float *state, const float *in, int arch)
{
int i;
int N;
float zrh[3*MAX_RNN_NEURONS_ALL];
float recur[3*MAX_RNN_NEURONS_ALL];
float *z;
float *r;
float *h;
celt_assert(3*recurrent_weights->nb_inputs == recurrent_weights->nb_outputs);
celt_assert(input_weights->nb_outputs == recurrent_weights->nb_outputs);
N = recurrent_weights->nb_inputs;
z = zrh;
r = &zrh[N];
h = &zrh[2*N];
celt_assert(recurrent_weights->nb_outputs <= 3*MAX_RNN_NEURONS_ALL);
celt_assert(in != state);
compute_linear(input_weights, zrh, in, arch);
compute_linear(recurrent_weights, recur, state, arch);
for (i=0;i<2*N;i++)
zrh[i] += recur[i];
compute_activation(zrh, zrh, 2*N, ACTIVATION_SIGMOID, arch);
for (i=0;i<N;i++)
h[i] += recur[2*N+i]*r[i];
compute_activation(h, h, N, ACTIVATION_TANH, arch);
for (i=0;i<N;i++)
h[i] = z[i]*state[i] + (1-z[i])*h[i];
for (i=0;i<N;i++)
state[i] = h[i];
}
void compute_glu(const LinearLayer *layer, float *output, const float *input, int arch)
{
int i;
float act2[MAX_INPUTS];
celt_assert(layer->nb_inputs == layer->nb_outputs);
compute_linear(layer, act2, input, arch);
compute_activation(act2, act2, layer->nb_outputs, ACTIVATION_SIGMOID, arch);
if (input == output) {
/* Give a vectorization hint to the compiler for the in-place case. */
for (i=0;i<layer->nb_outputs;i++) output[i] = output[i]*act2[i];
} else {
for (i=0;i<layer->nb_outputs;i++) output[i] = input[i]*act2[i];
}
}
#define MAX_CONV_INPUTS_ALL 1024
void compute_generic_conv1d(const LinearLayer *layer, float *output, float *mem, const float *input, int input_size, int activation, int arch)
{
float tmp[MAX_CONV_INPUTS_ALL];
celt_assert(input != output);
celt_assert(layer->nb_inputs <= MAX_CONV_INPUTS_ALL);
if (layer->nb_inputs!=input_size) RNN_COPY(tmp, mem, layer->nb_inputs-input_size);
RNN_COPY(&tmp[layer->nb_inputs-input_size], input, input_size);
compute_linear(layer, output, tmp, arch);
compute_activation(output, output, layer->nb_outputs, activation, arch);
if (layer->nb_inputs!=input_size) RNN_COPY(mem, &tmp[input_size], layer->nb_inputs-input_size);
}

View File

@@ -1,169 +0,0 @@
/* Copyright (c) 2018 Mozilla
Copyright (c) 2017 Jean-Marc Valin */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef NNET_H_
#define NNET_H_
#include <stddef.h>
#include "opus_types.h"
#define ACTIVATION_LINEAR 0
#define ACTIVATION_SIGMOID 1
#define ACTIVATION_TANH 2
#define ACTIVATION_RELU 3
#define ACTIVATION_SOFTMAX 4
#define ACTIVATION_SWISH 5
#define WEIGHT_BLOB_VERSION 0
#define WEIGHT_BLOCK_SIZE 64
typedef struct {
const char *name;
int type;
int size;
const void *data;
} WeightArray;
#define WEIGHT_TYPE_float 0
#define WEIGHT_TYPE_int 1
#define WEIGHT_TYPE_qweight 2
#define WEIGHT_TYPE_int8 3
typedef struct {
char head[4];
int version;
int type;
int size;
int block_size;
char name[44];
} WeightHead;
/* Generic sparse affine transformation. */
typedef struct {
const float *bias;
const float *subias;
const opus_int8 *weights;
const float *float_weights;
const int *weights_idx;
const float *diag;
const float *scale;
int nb_inputs;
int nb_outputs;
} LinearLayer;
/* Generic sparse affine transformation. */
typedef struct {
const float *bias;
const float *float_weights;
int in_channels;
int out_channels;
int ktime;
int kheight;
} Conv2dLayer;
/* Changes some symbol names to add the rnn_ prefix so we don't get conflicts with Opus. */
#define linear_init rnn_linear_init
#define conv2d_init rnn_conv2d_init
#define compute_generic_dense rnn_compute_generic_dense
#define compute_generic_gru rnn_compute_generic_gru
#define compute_generic_conv1d rnn_compute_generic_conv1d
#define compute_glu rnn_compute_glu
#define parse_weights rnn_parse_weights
#define compute_linear_c rnn_compute_linear_c
#define compute_activation_c rnn_compute_activation_c
#define compute_conv2d_c rnn_compute_conv2d_c
#define compute_linear_sse4_1 rnn_compute_linear_sse4_1
#define compute_activation_sse4_1 rnn_compute_activation_sse4_1
#define compute_conv2d_sse4_1 rnn_compute_conv2d_sse4_1
#define compute_linear_avx2 rnn_compute_linear_avx2
#define compute_activation_avx2 rnn_compute_activation_avx2
#define compute_conv2d_avx2 rnn_compute_conv2d_avx2
void compute_generic_dense(const LinearLayer *layer, float *output, const float *input, int activation, int arch);
void compute_generic_gru(const LinearLayer *input_weights, const LinearLayer *recurrent_weights, float *state, const float *in, int arch);
void compute_generic_conv1d(const LinearLayer *layer, float *output, float *mem, const float *input, int input_size, int activation, int arch);
void compute_glu(const LinearLayer *layer, float *output, const float *input, int arch);
int parse_weights(WeightArray **list, const void *data, int len);
int linear_init(LinearLayer *layer, const WeightArray *arrays,
const char *bias,
const char *subias,
const char *weights,
const char *float_weights,
const char *weights_idx,
const char *diag,
const char *scale,
int nb_inputs,
int nb_outputs);
int conv2d_init(Conv2dLayer *layer, const WeightArray *arrays,
const char *bias,
const char *float_weights,
int in_channels,
int out_channels,
int ktime,
int kheight);
void compute_linear_c(const LinearLayer *linear, float *out, const float *in);
void compute_activation_c(float *output, const float *input, int N, int activation);
void compute_conv2d_c(const Conv2dLayer *conv, float *out, float *mem, const float *in, int height, int hstride, int activation);
#ifdef RNN_ENABLE_X86_RTCD
#include "x86/dnn_x86.h"
#endif
#ifndef OVERRIDE_COMPUTE_LINEAR
#define compute_linear(linear, out, in, arch) ((void)(arch),compute_linear_c(linear, out, in))
#endif
#ifndef OVERRIDE_COMPUTE_ACTIVATION
#define compute_activation(output, input, N, activation, arch) ((void)(arch),compute_activation_c(output, input, N, activation))
#endif
#ifndef OVERRIDE_COMPUTE_CONV2D
#define compute_conv2d(conv, out, mem, in, height, hstride, activation, arch) ((void)(arch),compute_conv2d_c(conv, out, mem, in, height, hstride, activation))
#endif
#if defined(__x86_64__) && !defined(RNN_ENABLE_X86_RTCD) && !defined(__AVX2__)
#if defined(_MSC_VER)
#pragma message ("Only SSE and SSE2 are available. On newer machines, enable SSSE3/AVX/AVX2 to get better performance")
#else
#warning "Only SSE and SSE2 are available. On newer machines, enable SSSE3/AVX/AVX2 using -march= to get better performance"
#endif
#endif
#endif /* NNET_H_ */

View File

@@ -1,257 +0,0 @@
/* Copyright (c) 2018-2019 Mozilla
2023 Amazon */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef NNET_ARCH_H
#define NNET_ARCH_H
#include "nnet.h"
#include "arch.h"
#include "common.h"
#include "vec.h"
#define CAT_SUFFIX2(a,b) a ## b
#define CAT_SUFFIX(a,b) CAT_SUFFIX2(a, b)
#define RTCD_SUF(name) CAT_SUFFIX(name, RTCD_ARCH)
# if !defined(OPUS_GNUC_PREREQ)
# if defined(__GNUC__)&&defined(__GNUC_MINOR__)
# define OPUS_GNUC_PREREQ(_maj,_min) \
((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min))
# else
# define OPUS_GNUC_PREREQ(_maj,_min) 0
# endif
# endif
/* Force vectorization on for DNN code because some of the loops rely on
compiler vectorization rather than explicitly using intrinsics. */
#if OPUS_GNUC_PREREQ(5,1)
#define GCC_POP_OPTIONS
#pragma GCC push_options
#pragma GCC optimize("tree-vectorize")
#endif
#define MAX_ACTIVATIONS (4096)
static OPUS_INLINE void vec_swish(float *y, const float *x, int N)
{
int i;
float tmp[MAX_ACTIVATIONS];
celt_assert(N <= MAX_ACTIVATIONS);
vec_sigmoid(tmp, x, N);
for (i=0;i<N;i++)
y[i] = x[i]*tmp[i];
}
static OPUS_INLINE float relu(float x)
{
return x < 0 ? 0 : x;
}
/*#define HIGH_ACCURACY */
void RTCD_SUF(compute_activation_)(float *output, const float *input, int N, int activation)
{
int i;
if (activation == ACTIVATION_SIGMOID) {
#ifdef HIGH_ACCURACY
for (int n=0; n<N; n++)
{
output[n] = 1.f / (1 + exp(-input[n]));
}
#else
vec_sigmoid(output, input, N);
#endif
} else if (activation == ACTIVATION_TANH) {
#ifdef HIGH_ACCURACY
for (int n=0; n<N; n++)
{
output[n] = tanh(input[n]);
}
#else
vec_tanh(output, input, N);
#endif
} else if (activation == ACTIVATION_SWISH) {
vec_swish(output, input, N);
} else if (activation == ACTIVATION_RELU) {
for (i=0;i<N;i++)
output[i] = relu(input[i]);
} else if (activation == ACTIVATION_SOFTMAX) {
#ifdef SOFTMAX_HACK
RNN_COPY(output, input, N);
/*for (i=0;i<N;i++)
output[i] = input[i];*/
#else
float sum = 0;
softmax(output, input, N);
for (i=0;i<N;i++) {
sum += output[i];
}
sum = 1.f/(sum+1e-30);
for (i=0;i<N;i++)
output[i] = sum*output[i];
#endif
} else {
celt_assert(activation == ACTIVATION_LINEAR);
if (input != output) {
for (i=0;i<N;i++)
output[i] = input[i];
}
}
}
void RTCD_SUF(compute_linear_) (const LinearLayer *linear, float *out, const float *in)
{
int i, M, N;
const float *bias;
celt_assert(in != out);
bias = linear->bias;
M = linear->nb_inputs;
N = linear->nb_outputs;
if (linear->float_weights != NULL) {
if (linear->weights_idx != NULL) sparse_sgemv8x4(out, linear->float_weights, linear->weights_idx, N, in);
else sgemv(out, linear->float_weights, N, M, N, in);
} else if (linear->weights != NULL) {
if (linear->weights_idx != NULL) sparse_cgemv8x4(out, linear->weights, linear->weights_idx, linear->scale, N, M, in);
else cgemv8x4(out, linear->weights, linear->scale, N, M, in);
/* Only use SU biases on for integer matrices on SU archs. */
#ifdef USE_SU_BIAS
bias = linear->subias;
#endif
}
else RNN_CLEAR(out, N);
if (bias != NULL) {
for (i=0;i<N;i++) out[i] += bias[i];
}
if (linear->diag) {
/* Diag is only used for GRU recurrent weights. */
celt_assert(3*M == N);
for (i=0;i<M;i++) {
out[i] += linear->diag[i]*in[i];
out[i+M] += linear->diag[i+M]*in[i];
out[i+2*M] += linear->diag[i+2*M]*in[i];
}
}
}
/* Computes non-padded convolution for input [ ksize1 x in_channels x (len2+ksize2) ],
kernel [ out_channels x in_channels x ksize1 x ksize2 ],
storing the output as [ out_channels x len2 ].
We assume that the output dimension along the ksize1 axis is 1,
i.e. processing one frame at a time. */
static void conv2d_float(float *out, const float *weights, int in_channels, int out_channels, int ktime, int kheight, const float *in, int height, int hstride)
{
int i;
int in_stride;
in_stride = height+kheight-1;
for (i=0;i<out_channels;i++) {
int m;
RNN_CLEAR(&out[i*hstride], height);
for (m=0;m<in_channels;m++) {
int t;
for (t=0;t<ktime;t++) {
int h;
for (h=0;h<kheight;h++) {
int j;
for (j=0;j<height;j++) {
out[i*hstride + j] += weights[i*in_channels*ktime*kheight + m*ktime*kheight + t*kheight + h] *
in[t*in_channels*in_stride + m*in_stride + j + h];
}
}
}
}
}
}
/* There's no intrinsics in this function (or the one above) because the gcc (and hopefully other compiler) auto-vectorizer is smart enough to
produce the right code by itself based on the compile flags. */
static void conv2d_3x3_float(float *out, const float *weights, int in_channels, int out_channels, const float *in, int height, int hstride)
{
int i;
int in_stride;
int kheight, ktime;
kheight = ktime = 3;
in_stride = height+kheight-1;
for (i=0;i<out_channels;i++) {
int m;
RNN_CLEAR(&out[i*hstride], height);
for (m=0;m<in_channels;m++) {
int j;
for (j=0;j<height;j++) {
/* Unrolled version of previous function -- compiler will figure out the indexing simplifications. */
out[i*hstride + j] += weights[i*in_channels*ktime*kheight + m*ktime*kheight + 0*kheight + 0]*in[0*in_channels*in_stride + m*in_stride + j + 0]
+ weights[i*in_channels*ktime*kheight + m*ktime*kheight + 0*kheight + 1]*in[0*in_channels*in_stride + m*in_stride + j + 1]
+ weights[i*in_channels*ktime*kheight + m*ktime*kheight + 0*kheight + 2]*in[0*in_channels*in_stride + m*in_stride + j + 2]
+ weights[i*in_channels*ktime*kheight + m*ktime*kheight + 1*kheight + 0]*in[1*in_channels*in_stride + m*in_stride + j + 0]
+ weights[i*in_channels*ktime*kheight + m*ktime*kheight + 1*kheight + 1]*in[1*in_channels*in_stride + m*in_stride + j + 1]
+ weights[i*in_channels*ktime*kheight + m*ktime*kheight + 1*kheight + 2]*in[1*in_channels*in_stride + m*in_stride + j + 2]
+ weights[i*in_channels*ktime*kheight + m*ktime*kheight + 2*kheight + 0]*in[2*in_channels*in_stride + m*in_stride + j + 0]
+ weights[i*in_channels*ktime*kheight + m*ktime*kheight + 2*kheight + 1]*in[2*in_channels*in_stride + m*in_stride + j + 1]
+ weights[i*in_channels*ktime*kheight + m*ktime*kheight + 2*kheight + 2]*in[2*in_channels*in_stride + m*in_stride + j + 2];
}
}
}
}
#define MAX_CONV2D_INPUTS 8192
void RTCD_SUF(compute_conv2d_)(const Conv2dLayer *conv, float *out, float *mem, const float *in, int height, int hstride, int activation)
{
int i;
const float *bias;
float in_buf[MAX_CONV2D_INPUTS];
int time_stride;
celt_assert(in != out);
time_stride = conv->in_channels*(height+conv->kheight-1);
celt_assert(conv->ktime*time_stride <= MAX_CONV2D_INPUTS);
RNN_COPY(in_buf, mem, (conv->ktime-1)*time_stride);
RNN_COPY(&in_buf[(conv->ktime-1)*time_stride], in, time_stride);
RNN_COPY(mem, &in_buf[time_stride], (conv->ktime-1)*time_stride);
bias = conv->bias;
if (conv->kheight == 3 && conv->ktime == 3)
conv2d_3x3_float(out, conv->float_weights, conv->in_channels, conv->out_channels, in_buf, height, hstride);
else
conv2d_float(out, conv->float_weights, conv->in_channels, conv->out_channels, conv->ktime, conv->kheight, in_buf, height, hstride);
if (bias != NULL) {
for (i=0;i<conv->out_channels;i++) {
int j;
for (j=0;j<height;j++) out[i*hstride+j] += bias[i];
}
}
for (i=0;i<conv->out_channels;i++) {
RTCD_SUF(compute_activation_)(&out[i*hstride], &out[i*hstride], height, activation);
}
}
#ifdef GCC_POP_OPTIONS
#pragma GCC pop_options
#endif
#endif

View File

@@ -1,35 +0,0 @@
/* Copyright (c) 2018-2019 Mozilla
2023 Amazon */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define RTCD_ARCH c
#include "nnet_arch.h"

View File

@@ -1,159 +0,0 @@
/* (C) COPYRIGHT 1994-2002 Xiph.Org Foundation */
/* Modified by Jean-Marc Valin */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* opus_types.h based on ogg_types.h from libogg */
/**
@file opus_types.h
@brief Opus reference implementation types
*/
#ifndef OPUS_TYPES_H
#define OPUS_TYPES_H
/* Use the real stdint.h if it's there (taken from Paul Hsieh's pstdint.h) */
#if (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) || defined (HAVE_STDINT_H))
#include <stdint.h>
typedef int16_t opus_int16;
typedef uint16_t opus_uint16;
typedef int32_t opus_int32;
typedef uint32_t opus_uint32;
#elif defined(_WIN32)
# if defined(__CYGWIN__)
# include <_G_config.h>
typedef _G_int32_t opus_int32;
typedef _G_uint32_t opus_uint32;
typedef _G_int16 opus_int16;
typedef _G_uint16 opus_uint16;
# elif defined(__MINGW32__)
typedef short opus_int16;
typedef unsigned short opus_uint16;
typedef int opus_int32;
typedef unsigned int opus_uint32;
# elif defined(__MWERKS__)
typedef int opus_int32;
typedef unsigned int opus_uint32;
typedef short opus_int16;
typedef unsigned short opus_uint16;
# else
/* MSVC/Borland */
typedef __int32 opus_int32;
typedef unsigned __int32 opus_uint32;
typedef __int16 opus_int16;
typedef unsigned __int16 opus_uint16;
# endif
#elif defined(__MACOS__)
# include <sys/types.h>
typedef SInt16 opus_int16;
typedef UInt16 opus_uint16;
typedef SInt32 opus_int32;
typedef UInt32 opus_uint32;
#elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */
# include <sys/types.h>
typedef int16_t opus_int16;
typedef u_int16_t opus_uint16;
typedef int32_t opus_int32;
typedef u_int32_t opus_uint32;
#elif defined(__BEOS__)
/* Be */
# include <inttypes.h>
typedef int16 opus_int16;
typedef u_int16 opus_uint16;
typedef int32_t opus_int32;
typedef u_int32_t opus_uint32;
#elif defined (__EMX__)
/* OS/2 GCC */
typedef short opus_int16;
typedef unsigned short opus_uint16;
typedef int opus_int32;
typedef unsigned int opus_uint32;
#elif defined (DJGPP)
/* DJGPP */
typedef short opus_int16;
typedef unsigned short opus_uint16;
typedef int opus_int32;
typedef unsigned int opus_uint32;
#elif defined(R5900)
/* PS2 EE */
typedef int opus_int32;
typedef unsigned opus_uint32;
typedef short opus_int16;
typedef unsigned short opus_uint16;
#elif defined(__SYMBIAN32__)
/* Symbian GCC */
typedef signed short opus_int16;
typedef unsigned short opus_uint16;
typedef signed int opus_int32;
typedef unsigned int opus_uint32;
#elif defined(CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
typedef short opus_int16;
typedef unsigned short opus_uint16;
typedef long opus_int32;
typedef unsigned long opus_uint32;
#elif defined(CONFIG_TI_C6X)
typedef short opus_int16;
typedef unsigned short opus_uint16;
typedef int opus_int32;
typedef unsigned int opus_uint32;
#else
/* Give up, take a reasonable guess */
typedef short opus_int16;
typedef unsigned short opus_uint16;
typedef int opus_int32;
typedef unsigned int opus_uint32;
#endif
#define opus_int int /* used for counters etc; at least 16 bits */
#define opus_int64 long long
#define opus_int8 signed char
#define opus_uint unsigned int /* used for counters etc; at least 16 bits */
#define opus_uint64 unsigned long long
#define opus_uint8 unsigned char
#endif /* OPUS_TYPES_H */

View File

@@ -1,237 +0,0 @@
/* Copyright (c) 2023 Amazon */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <stdlib.h>
#include "nnet.h"
#define SPARSE_BLOCK_SIZE 32
static int parse_record(const void **data, int *len, WeightArray *array) {
WeightHead *h = (WeightHead *)*data;
if (*len < WEIGHT_BLOCK_SIZE) return -1;
if (h->block_size < h->size) return -1;
if (h->block_size > *len-WEIGHT_BLOCK_SIZE) return -1;
if (h->name[sizeof(h->name)-1] != 0) return -1;
if (h->size < 0) return -1;
array->name = h->name;
array->type = h->type;
array->size = h->size;
array->data = (void*)((unsigned char*)(*data)+WEIGHT_BLOCK_SIZE);
*data = (void*)((unsigned char*)*data + h->block_size+WEIGHT_BLOCK_SIZE);
*len -= h->block_size+WEIGHT_BLOCK_SIZE;
return array->size;
}
int parse_weights(WeightArray **list, const void *data, int len)
{
int nb_arrays=0;
int capacity=20;
*list = calloc(capacity*sizeof(WeightArray), 1);
while (len > 0) {
int ret;
WeightArray array = {NULL, 0, 0, 0};
ret = parse_record(&data, &len, &array);
if (ret > 0) {
if (nb_arrays+1 >= capacity) {
/* Make sure there's room for the ending NULL element too. */
capacity = capacity*3/2;
*list = realloc(*list, capacity*sizeof(WeightArray));
}
(*list)[nb_arrays++] = array;
} else {
free(*list);
*list = NULL;
return -1;
}
}
(*list)[nb_arrays].name=NULL;
return nb_arrays;
}
static const void *find_array_entry(const WeightArray *arrays, const char *name) {
while (arrays->name && strcmp(arrays->name, name) != 0) arrays++;
return arrays;
}
static const void *find_array_check(const WeightArray *arrays, const char *name, int size) {
const WeightArray *a = find_array_entry(arrays, name);
if (a->name && a->size == size) return a->data;
else return NULL;
}
static const void *opt_array_check(const WeightArray *arrays, const char *name, int size, int *error) {
const WeightArray *a = find_array_entry(arrays, name);
*error = (a->name != NULL && a->size != size);
if (a->name && a->size == size) return a->data;
else return NULL;
}
static const void *find_idx_check(const WeightArray *arrays, const char *name, int nb_in, int nb_out, int *total_blocks) {
int remain;
const int *idx;
const WeightArray *a = find_array_entry(arrays, name);
*total_blocks = 0;
if (a == NULL) return NULL;
idx = a->data;
remain = a->size/sizeof(int);
while (remain > 0) {
int nb_blocks;
int i;
nb_blocks = *idx++;
if (remain < nb_blocks+1) return NULL;
for (i=0;i<nb_blocks;i++) {
int pos = *idx++;
if (pos+3 >= nb_in || (pos&0x3)) return NULL;
}
nb_out -= 8;
remain -= nb_blocks+1;
*total_blocks += nb_blocks;
}
if (nb_out != 0) return NULL;
return a->data;
}
int linear_init(LinearLayer *layer, const WeightArray *arrays,
const char *bias,
const char *subias,
const char *weights,
const char *float_weights,
const char *weights_idx,
const char *diag,
const char *scale,
int nb_inputs,
int nb_outputs)
{
int err;
layer->bias = NULL;
layer->subias = NULL;
layer->weights = NULL;
layer->float_weights = NULL;
layer->weights_idx = NULL;
layer->diag = NULL;
layer->scale = NULL;
if (bias != NULL) {
if ((layer->bias = find_array_check(arrays, bias, nb_outputs*sizeof(layer->bias[0]))) == NULL) return 1;
}
if (subias != NULL) {
if ((layer->subias = find_array_check(arrays, subias, nb_outputs*sizeof(layer->subias[0]))) == NULL) return 1;
}
if (weights_idx != NULL) {
int total_blocks;
if ((layer->weights_idx = find_idx_check(arrays, weights_idx, nb_inputs, nb_outputs, &total_blocks)) == NULL) return 1;
if (weights != NULL) {
if ((layer->weights = find_array_check(arrays, weights, SPARSE_BLOCK_SIZE*total_blocks*sizeof(layer->weights[0]))) == NULL) return 1;
}
if (float_weights != NULL) {
layer->float_weights = opt_array_check(arrays, float_weights, SPARSE_BLOCK_SIZE*total_blocks*sizeof(layer->float_weights[0]), &err);
if (err) return 1;
}
} else {
if (weights != NULL) {
if ((layer->weights = find_array_check(arrays, weights, nb_inputs*nb_outputs*sizeof(layer->weights[0]))) == NULL) return 1;
}
if (float_weights != NULL) {
layer->float_weights = opt_array_check(arrays, float_weights, nb_inputs*nb_outputs*sizeof(layer->float_weights[0]), &err);
if (err) return 1;
}
}
if (diag != NULL) {
if ((layer->diag = find_array_check(arrays, diag, nb_outputs*sizeof(layer->diag[0]))) == NULL) return 1;
}
if (weights != NULL) {
if ((layer->scale = find_array_check(arrays, scale, nb_outputs*sizeof(layer->scale[0]))) == NULL) return 1;
}
layer->nb_inputs = nb_inputs;
layer->nb_outputs = nb_outputs;
return 0;
}
int conv2d_init(Conv2dLayer *layer, const WeightArray *arrays,
const char *bias,
const char *float_weights,
int in_channels,
int out_channels,
int ktime,
int kheight)
{
int err;
layer->bias = NULL;
layer->float_weights = NULL;
if (bias != NULL) {
if ((layer->bias = find_array_check(arrays, bias, out_channels*sizeof(layer->bias[0]))) == NULL) return 1;
}
if (float_weights != NULL) {
layer->float_weights = opt_array_check(arrays, float_weights, in_channels*out_channels*ktime*kheight*sizeof(layer->float_weights[0]), &err);
if (err) return 1;
}
layer->in_channels = in_channels;
layer->out_channels = out_channels;
layer->ktime = ktime;
layer->kheight = kheight;
return 0;
}
#if 0
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>
int main()
{
int fd;
void *data;
int len;
int nb_arrays;
int i;
WeightArray *list;
struct stat st;
const char *filename = "weights_blob.bin";
stat(filename, &st);
len = st.st_size;
fd = open(filename, O_RDONLY);
data = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
printf("size is %d\n", len);
nb_arrays = parse_weights(&list, data, len);
for (i=0;i<nb_arrays;i++) {
printf("found %s: size %d\n", list[i].name, list[i].size);
}
printf("%p\n", list[i].name);
free(list);
munmap(data, len);
close(fd);
return 0;
}
#endif

View File

@@ -1,528 +0,0 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2009 Xiph.Org Foundation
Written by Jean-Marc Valin */
/**
@file pitch.c
@brief Pitch analysis
*/
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "pitch.h"
#include "common.h"
#include "denoise.h"
#include "celt_lpc.h"
#include "math.h"
static void find_best_pitch(opus_val32 *xcorr, opus_val16 *y, int len,
int max_pitch, int *best_pitch
#ifdef FIXED_POINT
, int yshift, opus_val32 maxcorr
#endif
)
{
int i, j;
opus_val32 Syy=1;
opus_val16 best_num[2];
opus_val32 best_den[2];
#ifdef FIXED_POINT
int xshift;
xshift = celt_ilog2(maxcorr)-14;
#endif
best_num[0] = -1;
best_num[1] = -1;
best_den[0] = 0;
best_den[1] = 0;
best_pitch[0] = 0;
best_pitch[1] = 1;
for (j=0;j<len;j++)
Syy = ADD32(Syy, SHR32(MULT16_16(y[j],y[j]), yshift));
for (i=0;i<max_pitch;i++)
{
if (xcorr[i]>0)
{
opus_val16 num;
opus_val32 xcorr16;
xcorr16 = EXTRACT16(VSHR32(xcorr[i], xshift));
#ifndef FIXED_POINT
/* Considering the range of xcorr16, this should avoid both underflows
and overflows (inf) when squaring xcorr16 */
xcorr16 *= 1e-12f;
#endif
num = MULT16_16_Q15(xcorr16,xcorr16);
if (MULT16_32_Q15(num,best_den[1]) > MULT16_32_Q15(best_num[1],Syy))
{
if (MULT16_32_Q15(num,best_den[0]) > MULT16_32_Q15(best_num[0],Syy))
{
best_num[1] = best_num[0];
best_den[1] = best_den[0];
best_pitch[1] = best_pitch[0];
best_num[0] = num;
best_den[0] = Syy;
best_pitch[0] = i;
} else {
best_num[1] = num;
best_den[1] = Syy;
best_pitch[1] = i;
}
}
}
Syy += SHR32(MULT16_16(y[i+len],y[i+len]),yshift) - SHR32(MULT16_16(y[i],y[i]),yshift);
Syy = MAX32(1, Syy);
}
}
static void celt_fir5(const opus_val16 *x,
const opus_val16 *num,
opus_val16 *y,
int N,
opus_val16 *mem)
{
int i;
opus_val16 num0, num1, num2, num3, num4;
opus_val32 mem0, mem1, mem2, mem3, mem4;
num0=num[0];
num1=num[1];
num2=num[2];
num3=num[3];
num4=num[4];
mem0=mem[0];
mem1=mem[1];
mem2=mem[2];
mem3=mem[3];
mem4=mem[4];
for (i=0;i<N;i++)
{
opus_val32 sum = SHL32(EXTEND32(x[i]), SIG_SHIFT);
sum = MAC16_16(sum,num0,mem0);
sum = MAC16_16(sum,num1,mem1);
sum = MAC16_16(sum,num2,mem2);
sum = MAC16_16(sum,num3,mem3);
sum = MAC16_16(sum,num4,mem4);
mem4 = mem3;
mem3 = mem2;
mem2 = mem1;
mem1 = mem0;
mem0 = x[i];
y[i] = ROUND16(sum, SIG_SHIFT);
}
mem[0]=mem0;
mem[1]=mem1;
mem[2]=mem2;
mem[3]=mem3;
mem[4]=mem4;
}
void rnn_pitch_downsample(celt_sig *x[], opus_val16 *x_lp,
int len, int C)
{
int i;
opus_val32 ac[5];
opus_val16 tmp=Q15ONE;
opus_val16 lpc[4], mem[5]={0,0,0,0,0};
opus_val16 lpc2[5];
opus_val16 c1 = QCONST16(.8f,15);
#ifdef FIXED_POINT
int shift;
opus_val32 maxabs = celt_maxabs32(x[0], len);
if (C==2)
{
opus_val32 maxabs_1 = celt_maxabs32(x[1], len);
maxabs = MAX32(maxabs, maxabs_1);
}
if (maxabs<1)
maxabs=1;
shift = celt_ilog2(maxabs)-10;
if (shift<0)
shift=0;
if (C==2)
shift++;
#endif
for (i=1;i<len>>1;i++)
x_lp[i] = SHR32(HALF32(HALF32(x[0][(2*i-1)]+x[0][(2*i+1)])+x[0][2*i]), shift);
x_lp[0] = SHR32(HALF32(HALF32(x[0][1])+x[0][0]), shift);
if (C==2)
{
for (i=1;i<len>>1;i++)
x_lp[i] += SHR32(HALF32(HALF32(x[1][(2*i-1)]+x[1][(2*i+1)])+x[1][2*i]), shift);
x_lp[0] += SHR32(HALF32(HALF32(x[1][1])+x[1][0]), shift);
}
rnn_autocorr(x_lp, ac, NULL, 0,
4, len>>1);
/* Noise floor -40 dB */
#ifdef FIXED_POINT
ac[0] += SHR32(ac[0],13);
#else
ac[0] *= 1.0001f;
#endif
/* Lag windowing */
for (i=1;i<=4;i++)
{
/*ac[i] *= exp(-.5*(2*M_PI*.002*i)*(2*M_PI*.002*i));*/
#ifdef FIXED_POINT
ac[i] -= MULT16_32_Q15(2*i*i, ac[i]);
#else
ac[i] -= ac[i]*(.008f*i)*(.008f*i);
#endif
}
rnn_lpc(lpc, ac, 4);
for (i=0;i<4;i++)
{
tmp = MULT16_16_Q15(QCONST16(.9f,15), tmp);
lpc[i] = MULT16_16_Q15(lpc[i], tmp);
}
/* Add a zero */
lpc2[0] = lpc[0] + QCONST16(.8f,SIG_SHIFT);
lpc2[1] = lpc[1] + MULT16_16_Q15(c1,lpc[0]);
lpc2[2] = lpc[2] + MULT16_16_Q15(c1,lpc[1]);
lpc2[3] = lpc[3] + MULT16_16_Q15(c1,lpc[2]);
lpc2[4] = MULT16_16_Q15(c1,lpc[3]);
celt_fir5(x_lp, lpc2, x_lp, len>>1, mem);
}
void rnn_pitch_xcorr(const opus_val16 *_x, const opus_val16 *_y,
opus_val32 *xcorr, int len, int max_pitch)
{
#if 0 /* This is a simple version of the pitch correlation that should work
well on DSPs like Blackfin and TI C5x/C6x */
int i, j;
#ifdef FIXED_POINT
opus_val32 maxcorr=1;
#endif
for (i=0;i<max_pitch;i++)
{
opus_val32 sum = 0;
for (j=0;j<len;j++)
sum = MAC16_16(sum, _x[j], _y[i+j]);
xcorr[i] = sum;
#ifdef FIXED_POINT
maxcorr = MAX32(maxcorr, sum);
#endif
}
#ifdef FIXED_POINT
return maxcorr;
#endif
#else /* Unrolled version of the pitch correlation -- runs faster on x86 and ARM */
int i;
/*The EDSP version requires that max_pitch is at least 1, and that _x is
32-bit aligned.
Since it's hard to put asserts in assembly, put them here.*/
#ifdef FIXED_POINT
opus_val32 maxcorr=1;
#endif
celt_assert(max_pitch>0);
celt_assert((((unsigned char *)_x-(unsigned char *)NULL)&3)==0);
for (i=0;i<max_pitch-3;i+=4)
{
opus_val32 sum[4]={0,0,0,0};
xcorr_kernel(_x, _y+i, sum, len);
xcorr[i]=sum[0];
xcorr[i+1]=sum[1];
xcorr[i+2]=sum[2];
xcorr[i+3]=sum[3];
#ifdef FIXED_POINT
sum[0] = MAX32(sum[0], sum[1]);
sum[2] = MAX32(sum[2], sum[3]);
sum[0] = MAX32(sum[0], sum[2]);
maxcorr = MAX32(maxcorr, sum[0]);
#endif
}
/* In case max_pitch isn't a multiple of 4, do non-unrolled version. */
for (;i<max_pitch;i++)
{
opus_val32 sum;
sum = celt_inner_prod(_x, _y+i, len);
xcorr[i] = sum;
#ifdef FIXED_POINT
maxcorr = MAX32(maxcorr, sum);
#endif
}
#ifdef FIXED_POINT
return maxcorr;
#endif
#endif
}
void rnn_pitch_search(const opus_val16 *x_lp, opus_val16 *y,
int len, int max_pitch, int *pitch)
{
int i, j;
int lag;
int best_pitch[2]={0,0};
#ifdef FIXED_POINT
opus_val32 maxcorr;
opus_val32 xmax, ymax;
int shift=0;
#endif
int offset;
opus_val16 x_lp4[PITCH_FRAME_SIZE>>2];
opus_val16 y_lp4[(PITCH_FRAME_SIZE+PITCH_MAX_PERIOD)>>2];
opus_val32 xcorr[PITCH_MAX_PERIOD>>1];
celt_assert(len <= PITCH_FRAME_SIZE);
celt_assert(max_pitch <= PITCH_MAX_PERIOD);
celt_assert(len>0);
celt_assert(max_pitch>0);
lag = len+max_pitch;
/* Downsample by 2 again */
for (j=0;j<len>>2;j++)
x_lp4[j] = x_lp[2*j];
for (j=0;j<lag>>2;j++)
y_lp4[j] = y[2*j];
#ifdef FIXED_POINT
xmax = celt_maxabs16(x_lp4, len>>2);
ymax = celt_maxabs16(y_lp4, lag>>2);
shift = celt_ilog2(MAX32(1, MAX32(xmax, ymax)))-11;
if (shift>0)
{
for (j=0;j<len>>2;j++)
x_lp4[j] = SHR16(x_lp4[j], shift);
for (j=0;j<lag>>2;j++)
y_lp4[j] = SHR16(y_lp4[j], shift);
/* Use double the shift for a MAC */
shift *= 2;
} else {
shift = 0;
}
#endif
/* Coarse search with 4x decimation */
#ifdef FIXED_POINT
maxcorr =
#endif
rnn_pitch_xcorr(x_lp4, y_lp4, xcorr, len>>2, max_pitch>>2);
find_best_pitch(xcorr, y_lp4, len>>2, max_pitch>>2, best_pitch
#ifdef FIXED_POINT
, 0, maxcorr
#endif
);
/* Finer search with 2x decimation */
#ifdef FIXED_POINT
maxcorr=1;
#endif
for (i=0;i<max_pitch>>1;i++)
{
opus_val32 sum;
xcorr[i] = 0;
if (abs(i-2*best_pitch[0])>2 && abs(i-2*best_pitch[1])>2)
continue;
#ifdef FIXED_POINT
sum = 0;
for (j=0;j<len>>1;j++)
sum += SHR32(MULT16_16(x_lp[j],y[i+j]), shift);
#else
sum = celt_inner_prod(x_lp, y+i, len>>1);
#endif
xcorr[i] = MAX32(-1, sum);
#ifdef FIXED_POINT
maxcorr = MAX32(maxcorr, sum);
#endif
}
find_best_pitch(xcorr, y, len>>1, max_pitch>>1, best_pitch
#ifdef FIXED_POINT
, shift+1, maxcorr
#endif
);
/* Refine by pseudo-interpolation */
if (best_pitch[0]>0 && best_pitch[0]<(max_pitch>>1)-1)
{
opus_val32 a, b, c;
a = xcorr[best_pitch[0]-1];
b = xcorr[best_pitch[0]];
c = xcorr[best_pitch[0]+1];
if ((c-a) > MULT16_32_Q15(QCONST16(.7f,15),b-a))
offset = 1;
else if ((a-c) > MULT16_32_Q15(QCONST16(.7f,15),b-c))
offset = -1;
else
offset = 0;
} else {
offset = 0;
}
*pitch = 2*best_pitch[0]-offset;
}
#ifdef FIXED_POINT
static opus_val16 compute_pitch_gain(opus_val32 xy, opus_val32 xx, opus_val32 yy)
{
opus_val32 x2y2;
int sx, sy, shift;
opus_val32 g;
opus_val16 den;
if (xy == 0 || xx == 0 || yy == 0)
return 0;
sx = celt_ilog2(xx)-14;
sy = celt_ilog2(yy)-14;
shift = sx + sy;
x2y2 = SHR32(MULT16_16(VSHR32(xx, sx), VSHR32(yy, sy)), 14);
if (shift & 1) {
if (x2y2 < 32768)
{
x2y2 <<= 1;
shift--;
} else {
x2y2 >>= 1;
shift++;
}
}
den = celt_rsqrt_norm(x2y2);
g = MULT16_32_Q15(den, xy);
g = VSHR32(g, (shift>>1)-1);
return EXTRACT16(MIN32(g, Q15ONE));
}
#else
static opus_val16 compute_pitch_gain(opus_val32 xy, opus_val32 xx, opus_val32 yy)
{
return xy/sqrt(1+xx*yy);
}
#endif
static const int second_check[16] = {0, 0, 3, 2, 3, 2, 5, 2, 3, 2, 3, 2, 5, 2, 3, 2};
opus_val16 rnn_remove_doubling(opus_val16 *x, int maxperiod, int minperiod,
int N, int *T0_, int prev_period, opus_val16 prev_gain)
{
int k, i, T, T0;
opus_val16 g, g0;
opus_val16 pg;
opus_val32 xy,xx,yy,xy2;
opus_val32 xcorr[3];
opus_val32 best_xy, best_yy;
int offset;
int minperiod0;
opus_val32 yy_lookup[PITCH_MAX_PERIOD+1];
celt_assert(maxperiod <= PITCH_MAX_PERIOD);
minperiod0 = minperiod;
maxperiod /= 2;
minperiod /= 2;
*T0_ /= 2;
prev_period /= 2;
N /= 2;
x += maxperiod;
if (*T0_>=maxperiod)
*T0_=maxperiod-1;
T = T0 = *T0_;
dual_inner_prod(x, x, x-T0, N, &xx, &xy);
yy_lookup[0] = xx;
yy=xx;
for (i=1;i<=maxperiod;i++)
{
yy = yy+MULT16_16(x[-i],x[-i])-MULT16_16(x[N-i],x[N-i]);
yy_lookup[i] = MAX32(0, yy);
}
yy = yy_lookup[T0];
best_xy = xy;
best_yy = yy;
g = g0 = compute_pitch_gain(xy, xx, yy);
/* Look for any pitch at T/k */
for (k=2;k<=15;k++)
{
int T1, T1b;
opus_val16 g1;
opus_val16 cont=0;
opus_val16 thresh;
T1 = (2*T0+k)/(2*k);
if (T1 < minperiod)
break;
/* Look for another strong correlation at T1b */
if (k==2)
{
if (T1+T0>maxperiod)
T1b = T0;
else
T1b = T0+T1;
} else
{
T1b = (2*second_check[k]*T0+k)/(2*k);
}
dual_inner_prod(x, &x[-T1], &x[-T1b], N, &xy, &xy2);
xy = HALF32(xy + xy2);
yy = HALF32(yy_lookup[T1] + yy_lookup[T1b]);
g1 = compute_pitch_gain(xy, xx, yy);
if (abs(T1-prev_period)<=1)
cont = prev_gain;
else if (abs(T1-prev_period)<=2 && 5*k*k < T0)
cont = HALF16(prev_gain);
else
cont = 0;
thresh = MAX16(QCONST16(.3f,15), MULT16_16_Q15(QCONST16(.7f,15),g0)-cont);
/* Bias against very high pitch (very short period) to avoid false-positives
due to short-term correlation */
if (T1<3*minperiod)
thresh = MAX16(QCONST16(.4f,15), MULT16_16_Q15(QCONST16(.85f,15),g0)-cont);
else if (T1<2*minperiod)
thresh = MAX16(QCONST16(.5f,15), MULT16_16_Q15(QCONST16(.9f,15),g0)-cont);
if (g1 > thresh)
{
best_xy = xy;
best_yy = yy;
T = T1;
g = g1;
}
}
best_xy = MAX32(0, best_xy);
if (best_yy <= best_xy)
pg = Q15ONE;
else
pg = best_xy/(best_yy+1);
for (k=0;k<3;k++)
xcorr[k] = celt_inner_prod(x, x-(T+k-1), N);
if ((xcorr[2]-xcorr[0]) > MULT16_32_Q15(QCONST16(.7f,15),xcorr[1]-xcorr[0]))
offset = 1;
else if ((xcorr[0]-xcorr[2]) > MULT16_32_Q15(QCONST16(.7f,15),xcorr[1]-xcorr[2]))
offset = -1;
else
offset = 0;
if (pg > g)
pg = g;
*T0_ = 2*T+offset;
if (*T0_<minperiod0)
*T0_=minperiod0;
return pg;
}

View File

@@ -1,147 +0,0 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2009 Xiph.Org Foundation
Written by Jean-Marc Valin */
/**
@file pitch.h
@brief Pitch analysis
*/
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PITCH_H
#define PITCH_H
#include "arch.h"
void rnn_pitch_downsample(celt_sig *x[], opus_val16 *x_lp,
int len, int C);
void rnn_pitch_search(const opus_val16 *x_lp, opus_val16 *y,
int len, int max_pitch, int *pitch);
opus_val16 rnn_remove_doubling(opus_val16 *x, int maxperiod, int minperiod,
int N, int *T0, int prev_period, opus_val16 prev_gain);
/* OPT: This is the kernel you really want to optimize. It gets used a lot
by the prefilter and by the PLC. */
static OPUS_INLINE void xcorr_kernel(const opus_val16 * x, const opus_val16 * y, opus_val32 sum[4], int len)
{
int j;
opus_val16 y_0, y_1, y_2, y_3;
celt_assert(len>=3);
y_3=0; /* gcc doesn't realize that y_3 can't be used uninitialized */
y_0=*y++;
y_1=*y++;
y_2=*y++;
for (j=0;j<len-3;j+=4)
{
opus_val16 tmp;
tmp = *x++;
y_3=*y++;
sum[0] = MAC16_16(sum[0],tmp,y_0);
sum[1] = MAC16_16(sum[1],tmp,y_1);
sum[2] = MAC16_16(sum[2],tmp,y_2);
sum[3] = MAC16_16(sum[3],tmp,y_3);
tmp=*x++;
y_0=*y++;
sum[0] = MAC16_16(sum[0],tmp,y_1);
sum[1] = MAC16_16(sum[1],tmp,y_2);
sum[2] = MAC16_16(sum[2],tmp,y_3);
sum[3] = MAC16_16(sum[3],tmp,y_0);
tmp=*x++;
y_1=*y++;
sum[0] = MAC16_16(sum[0],tmp,y_2);
sum[1] = MAC16_16(sum[1],tmp,y_3);
sum[2] = MAC16_16(sum[2],tmp,y_0);
sum[3] = MAC16_16(sum[3],tmp,y_1);
tmp=*x++;
y_2=*y++;
sum[0] = MAC16_16(sum[0],tmp,y_3);
sum[1] = MAC16_16(sum[1],tmp,y_0);
sum[2] = MAC16_16(sum[2],tmp,y_1);
sum[3] = MAC16_16(sum[3],tmp,y_2);
}
if (j++<len)
{
opus_val16 tmp = *x++;
y_3=*y++;
sum[0] = MAC16_16(sum[0],tmp,y_0);
sum[1] = MAC16_16(sum[1],tmp,y_1);
sum[2] = MAC16_16(sum[2],tmp,y_2);
sum[3] = MAC16_16(sum[3],tmp,y_3);
}
if (j++<len)
{
opus_val16 tmp=*x++;
y_0=*y++;
sum[0] = MAC16_16(sum[0],tmp,y_1);
sum[1] = MAC16_16(sum[1],tmp,y_2);
sum[2] = MAC16_16(sum[2],tmp,y_3);
sum[3] = MAC16_16(sum[3],tmp,y_0);
}
if (j<len)
{
opus_val16 tmp=*x++;
y_1=*y++;
sum[0] = MAC16_16(sum[0],tmp,y_2);
sum[1] = MAC16_16(sum[1],tmp,y_3);
sum[2] = MAC16_16(sum[2],tmp,y_0);
sum[3] = MAC16_16(sum[3],tmp,y_1);
}
}
static OPUS_INLINE void dual_inner_prod(const opus_val16 *x, const opus_val16 *y01, const opus_val16 *y02,
int N, opus_val32 *xy1, opus_val32 *xy2)
{
int i;
opus_val32 xy01=0;
opus_val32 xy02=0;
for (i=0;i<N;i++)
{
xy01 = MAC16_16(xy01, x[i], y01[i]);
xy02 = MAC16_16(xy02, x[i], y02[i]);
}
*xy1 = xy01;
*xy2 = xy02;
}
/*We make sure a C version is always available for cases where the overhead of
vectorization and passing around an arch flag aren't worth it.*/
static OPUS_INLINE opus_val32 celt_inner_prod(const opus_val16 *x,
const opus_val16 *y, int N)
{
int i;
opus_val32 xy=0;
for (i=0;i<N;i++)
xy = MAC16_16(xy, x[i], y[i]);
return xy;
}
void rnn_pitch_xcorr(const opus_val16 *_x, const opus_val16 *_y,
opus_val32 *xcorr, int len, int max_pitch);
#endif

View File

@@ -1,60 +0,0 @@
/* Copyright (c) 2008-2011 Octasic Inc.
2012-2017 Jean-Marc Valin */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <math.h>
#include "opus_types.h"
#include "common.h"
#include "arch.h"
#include "rnn.h"
#include "rnnoise_data.h"
#include <stdio.h>
#define INPUT_SIZE 42
void compute_rnn(const RNNoise *model, RNNState *rnn, float *gains, float *vad, const float *input, int arch) {
float tmp[MAX_NEURONS];
float cat[CONV2_OUT_SIZE + GRU1_OUT_SIZE + GRU2_OUT_SIZE + GRU3_OUT_SIZE];
/*for (int i=0;i<INPUT_SIZE;i++) printf("%f ", input[i]);printf("\n");*/
compute_generic_conv1d(&model->conv1, tmp, rnn->conv1_state, input, CONV1_IN_SIZE, ACTIVATION_TANH, arch);
compute_generic_conv1d(&model->conv2, cat, rnn->conv2_state, tmp, CONV2_IN_SIZE, ACTIVATION_TANH, arch);
compute_generic_gru(&model->gru1_input, &model->gru1_recurrent, rnn->gru1_state, cat, arch);
compute_generic_gru(&model->gru2_input, &model->gru2_recurrent, rnn->gru2_state, rnn->gru1_state, arch);
compute_generic_gru(&model->gru3_input, &model->gru3_recurrent, rnn->gru3_state, rnn->gru2_state, arch);
RNN_COPY(&cat[CONV2_OUT_SIZE], rnn->gru1_state, GRU1_OUT_SIZE);
RNN_COPY(&cat[CONV2_OUT_SIZE+GRU1_OUT_SIZE], rnn->gru2_state, GRU2_OUT_SIZE);
RNN_COPY(&cat[CONV2_OUT_SIZE+GRU1_OUT_SIZE+GRU2_OUT_SIZE], rnn->gru3_state, GRU3_OUT_SIZE);
compute_generic_dense(&model->dense_out, gains, cat, ACTIVATION_SIGMOID, arch);
compute_generic_dense(&model->vad_dense, vad, cat, ACTIVATION_SIGMOID, arch);
/*for (int i=0;i<22;i++) printf("%f ", gains[i]);printf("\n");*/
/*printf("%f\n", *vad);*/
}

View File

@@ -1,49 +0,0 @@
/* Copyright (c) 2017 Jean-Marc Valin */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef RNN_H_
#define RNN_H_
#include "rnnoise.h"
#include "rnnoise_data.h"
#include "opus_types.h"
#define WEIGHTS_SCALE (1.f/256)
#define MAX_NEURONS 1024
typedef struct {
float conv1_state[CONV1_STATE_SIZE];
float conv2_state[CONV2_STATE_SIZE];
float gru1_state[GRU1_STATE_SIZE];
float gru2_state[GRU2_STATE_SIZE];
float gru3_state[GRU3_STATE_SIZE];
} RNNState;
void compute_rnn(const RNNoise *model, RNNState *rnn, float *gains, float *vad, const float *input, int arch);
#endif /* RNN_H_ */

View File

@@ -1,131 +0,0 @@
/* Copyright (c) 2018 Gregor Richards
* Copyright (c) 2017 Mozilla */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef RNNOISE_H
#define RNNOISE_H 1
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef RNNOISE_EXPORT
# if defined(WIN32)
# if defined(RNNOISE_BUILD) && defined(DLL_EXPORT)
# define RNNOISE_EXPORT __declspec(dllexport)
# else
# define RNNOISE_EXPORT
# endif
# elif defined(__GNUC__) && defined(RNNOISE_BUILD)
# define RNNOISE_EXPORT __attribute__ ((visibility ("default")))
# else
# define RNNOISE_EXPORT
# endif
#endif
typedef struct DenoiseState DenoiseState;
typedef struct RNNModel RNNModel;
/**
* Return the size of DenoiseState
*/
RNNOISE_EXPORT int rnnoise_get_size(void);
/**
* Return the number of samples processed by rnnoise_process_frame at a time
*/
RNNOISE_EXPORT int rnnoise_get_frame_size(void);
/**
* Initializes a pre-allocated DenoiseState
*
* If model is NULL the default model is used.
*
* See: rnnoise_create() and rnnoise_model_from_file()
*/
RNNOISE_EXPORT int rnnoise_init(DenoiseState *st, RNNModel *model);
/**
* Allocate and initialize a DenoiseState
*
* If model is NULL the default model is used.
*
* The returned pointer MUST be freed with rnnoise_destroy().
*/
RNNOISE_EXPORT DenoiseState *rnnoise_create(RNNModel *model);
/**
* Free a DenoiseState produced by rnnoise_create.
*
* The optional custom model must be freed by rnnoise_model_free() after.
*/
RNNOISE_EXPORT void rnnoise_destroy(DenoiseState *st);
/**
* Denoise a frame of samples
*
* in and out must be at least rnnoise_get_frame_size() large.
*/
RNNOISE_EXPORT float rnnoise_process_frame(DenoiseState *st, float *out, const float *in);
/**
* Load a model from a memory buffer
*
* It must be deallocated with rnnoise_model_free() and the buffer must remain
* valid until after the returned object is destroyed.
*/
RNNOISE_EXPORT RNNModel *rnnoise_model_from_buffer(const void *ptr, int len);
/**
* Load a model from a file
*
* It must be deallocated with rnnoise_model_free() and the file must not be
* closed until the returned object is destroyed.
*/
RNNOISE_EXPORT RNNModel *rnnoise_model_from_file(FILE *f);
/**
* Load a model from a file name
*
* It must be deallocated with rnnoise_model_free()
*/
RNNOISE_EXPORT RNNModel *rnnoise_model_from_filename(const char *filename);
/**
* Free a custom model
*
* It must be called after all the DenoiseStates referring to it are freed.
*/
RNNOISE_EXPORT void rnnoise_model_free(RNNModel *model);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,55 +0,0 @@
#ifndef RNNOISE_DATA_H
#define RNNOISE_DATA_H
#include "nnet.h"
#define CONV1_OUT_SIZE 128
#define CONV1_IN_SIZE 65
#define CONV1_STATE_SIZE (65 * (2))
#define CONV1_DELAY 1
#define CONV2_OUT_SIZE 384
#define CONV2_IN_SIZE 128
#define CONV2_STATE_SIZE (128 * (2))
#define CONV2_DELAY 1
#define GRU1_OUT_SIZE 384
#define GRU1_STATE_SIZE 384
#define GRU2_OUT_SIZE 384
#define GRU2_STATE_SIZE 384
#define GRU3_OUT_SIZE 384
#define GRU3_STATE_SIZE 384
#define DENSE_OUT_OUT_SIZE 32
#define VAD_DENSE_OUT_SIZE 1
typedef struct {
LinearLayer conv1;
LinearLayer conv2;
LinearLayer gru1_input;
LinearLayer gru1_recurrent;
LinearLayer gru2_input;
LinearLayer gru2_recurrent;
LinearLayer gru3_input;
LinearLayer gru3_recurrent;
LinearLayer dense_out;
LinearLayer vad_dense;
} RNNoise;
int init_rnnoise(RNNoise *model, const WeightArray *arrays);
#endif /* RNNOISE_DATA_H */

File diff suppressed because it is too large Load Diff

View File

@@ -1,874 +0,0 @@
/* The contents of this file was automatically generated by dump_rnnoise_tables.c*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "kiss_fft.h"
static const arch_fft_state arch_fft = {0, NULL};
static const opus_int32 fft_bitrev[960] = {
0, 192, 384, 576, 768, 64, 256, 448, 640, 832, 128, 320, 512, 704, 896,
16, 208, 400, 592, 784, 80, 272, 464, 656, 848, 144, 336, 528, 720, 912,
32, 224, 416, 608, 800, 96, 288, 480, 672, 864, 160, 352, 544, 736, 928,
48, 240, 432, 624, 816, 112, 304, 496, 688, 880, 176, 368, 560, 752, 944,
4, 196, 388, 580, 772, 68, 260, 452, 644, 836, 132, 324, 516, 708, 900,
20, 212, 404, 596, 788, 84, 276, 468, 660, 852, 148, 340, 532, 724, 916,
36, 228, 420, 612, 804, 100, 292, 484, 676, 868, 164, 356, 548, 740, 932,
52, 244, 436, 628, 820, 116, 308, 500, 692, 884, 180, 372, 564, 756, 948,
8, 200, 392, 584, 776, 72, 264, 456, 648, 840, 136, 328, 520, 712, 904,
24, 216, 408, 600, 792, 88, 280, 472, 664, 856, 152, 344, 536, 728, 920,
40, 232, 424, 616, 808, 104, 296, 488, 680, 872, 168, 360, 552, 744, 936,
56, 248, 440, 632, 824, 120, 312, 504, 696, 888, 184, 376, 568, 760, 952,
12, 204, 396, 588, 780, 76, 268, 460, 652, 844, 140, 332, 524, 716, 908,
28, 220, 412, 604, 796, 92, 284, 476, 668, 860, 156, 348, 540, 732, 924,
44, 236, 428, 620, 812, 108, 300, 492, 684, 876, 172, 364, 556, 748, 940,
60, 252, 444, 636, 828, 124, 316, 508, 700, 892, 188, 380, 572, 764, 956,
1, 193, 385, 577, 769, 65, 257, 449, 641, 833, 129, 321, 513, 705, 897,
17, 209, 401, 593, 785, 81, 273, 465, 657, 849, 145, 337, 529, 721, 913,
33, 225, 417, 609, 801, 97, 289, 481, 673, 865, 161, 353, 545, 737, 929,
49, 241, 433, 625, 817, 113, 305, 497, 689, 881, 177, 369, 561, 753, 945,
5, 197, 389, 581, 773, 69, 261, 453, 645, 837, 133, 325, 517, 709, 901,
21, 213, 405, 597, 789, 85, 277, 469, 661, 853, 149, 341, 533, 725, 917,
37, 229, 421, 613, 805, 101, 293, 485, 677, 869, 165, 357, 549, 741, 933,
53, 245, 437, 629, 821, 117, 309, 501, 693, 885, 181, 373, 565, 757, 949,
9, 201, 393, 585, 777, 73, 265, 457, 649, 841, 137, 329, 521, 713, 905,
25, 217, 409, 601, 793, 89, 281, 473, 665, 857, 153, 345, 537, 729, 921,
41, 233, 425, 617, 809, 105, 297, 489, 681, 873, 169, 361, 553, 745, 937,
57, 249, 441, 633, 825, 121, 313, 505, 697, 889, 185, 377, 569, 761, 953,
13, 205, 397, 589, 781, 77, 269, 461, 653, 845, 141, 333, 525, 717, 909,
29, 221, 413, 605, 797, 93, 285, 477, 669, 861, 157, 349, 541, 733, 925,
45, 237, 429, 621, 813, 109, 301, 493, 685, 877, 173, 365, 557, 749, 941,
61, 253, 445, 637, 829, 125, 317, 509, 701, 893, 189, 381, 573, 765, 957,
2, 194, 386, 578, 770, 66, 258, 450, 642, 834, 130, 322, 514, 706, 898,
18, 210, 402, 594, 786, 82, 274, 466, 658, 850, 146, 338, 530, 722, 914,
34, 226, 418, 610, 802, 98, 290, 482, 674, 866, 162, 354, 546, 738, 930,
50, 242, 434, 626, 818, 114, 306, 498, 690, 882, 178, 370, 562, 754, 946,
6, 198, 390, 582, 774, 70, 262, 454, 646, 838, 134, 326, 518, 710, 902,
22, 214, 406, 598, 790, 86, 278, 470, 662, 854, 150, 342, 534, 726, 918,
38, 230, 422, 614, 806, 102, 294, 486, 678, 870, 166, 358, 550, 742, 934,
54, 246, 438, 630, 822, 118, 310, 502, 694, 886, 182, 374, 566, 758, 950,
10, 202, 394, 586, 778, 74, 266, 458, 650, 842, 138, 330, 522, 714, 906,
26, 218, 410, 602, 794, 90, 282, 474, 666, 858, 154, 346, 538, 730, 922,
42, 234, 426, 618, 810, 106, 298, 490, 682, 874, 170, 362, 554, 746, 938,
58, 250, 442, 634, 826, 122, 314, 506, 698, 890, 186, 378, 570, 762, 954,
14, 206, 398, 590, 782, 78, 270, 462, 654, 846, 142, 334, 526, 718, 910,
30, 222, 414, 606, 798, 94, 286, 478, 670, 862, 158, 350, 542, 734, 926,
46, 238, 430, 622, 814, 110, 302, 494, 686, 878, 174, 366, 558, 750, 942,
62, 254, 446, 638, 830, 126, 318, 510, 702, 894, 190, 382, 574, 766, 958,
3, 195, 387, 579, 771, 67, 259, 451, 643, 835, 131, 323, 515, 707, 899,
19, 211, 403, 595, 787, 83, 275, 467, 659, 851, 147, 339, 531, 723, 915,
35, 227, 419, 611, 803, 99, 291, 483, 675, 867, 163, 355, 547, 739, 931,
51, 243, 435, 627, 819, 115, 307, 499, 691, 883, 179, 371, 563, 755, 947,
7, 199, 391, 583, 775, 71, 263, 455, 647, 839, 135, 327, 519, 711, 903,
23, 215, 407, 599, 791, 87, 279, 471, 663, 855, 151, 343, 535, 727, 919,
39, 231, 423, 615, 807, 103, 295, 487, 679, 871, 167, 359, 551, 743, 935,
55, 247, 439, 631, 823, 119, 311, 503, 695, 887, 183, 375, 567, 759, 951,
11, 203, 395, 587, 779, 75, 267, 459, 651, 843, 139, 331, 523, 715, 907,
27, 219, 411, 603, 795, 91, 283, 475, 667, 859, 155, 347, 539, 731, 923,
43, 235, 427, 619, 811, 107, 299, 491, 683, 875, 171, 363, 555, 747, 939,
59, 251, 443, 635, 827, 123, 315, 507, 699, 891, 187, 379, 571, 763, 955,
15, 207, 399, 591, 783, 79, 271, 463, 655, 847, 143, 335, 527, 719, 911,
31, 223, 415, 607, 799, 95, 287, 479, 671, 863, 159, 351, 543, 735, 927,
47, 239, 431, 623, 815, 111, 303, 495, 687, 879, 175, 367, 559, 751, 943,
63, 255, 447, 639, 831, 127, 319, 511, 703, 895, 191, 383, 575, 767, 959,
};
static const kiss_twiddle_cpx fft_twiddles[960] = {
{1.00000000f, -0.00000000f}, {0.999978602f, -0.00654493785f},
{0.999914348f, -0.0130895954f}, {0.999807239f, -0.0196336918f},
{0.999657333f, -0.0261769481f}, {0.999464571f, -0.0327190831f},
{0.999229014f, -0.0392598175f}, {0.998950660f, -0.0457988679f},
{0.998629510f, -0.0523359552f}, {0.998265624f, -0.0588708036f},
{0.997858942f, -0.0654031262f}, {0.997409463f, -0.0719326511f},
{0.996917307f, -0.0784590989f}, {0.996382475f, -0.0849821791f},
{0.995804906f, -0.0915016159f}, {0.995184720f, -0.0980171412f},
{0.994521916f, -0.104528464f}, {0.993816435f, -0.111035310f},
{0.993068457f, -0.117537394f}, {0.992277920f, -0.124034449f},
{0.991444886f, -0.130526185f}, {0.990569353f, -0.137012348f},
{0.989651382f, -0.143492624f}, {0.988691032f, -0.149966761f},
{0.987688363f, -0.156434461f}, {0.986643314f, -0.162895471f},
{0.985556066f, -0.169349506f}, {0.984426558f, -0.175796285f},
{0.983254910f, -0.182235524f}, {0.982041121f, -0.188666970f},
{0.980785251f, -0.195090324f}, {0.979487419f, -0.201505318f},
{0.978147626f, -0.207911685f}, {0.976765871f, -0.214309156f},
{0.975342333f, -0.220697433f}, {0.973876953f, -0.227076262f},
{0.972369909f, -0.233445361f}, {0.970821202f, -0.239804462f},
{0.969230890f, -0.246153295f}, {0.967599094f, -0.252491564f},
{0.965925813f, -0.258819044f}, {0.964211166f, -0.265135437f},
{0.962455213f, -0.271440446f}, {0.960658073f, -0.277733833f},
{0.958819747f, -0.284015357f}, {0.956940353f, -0.290284663f},
{0.955019951f, -0.296541572f}, {0.953058660f, -0.302785784f},
{0.951056540f, -0.309017003f}, {0.949013650f, -0.315234989f},
{0.946930110f, -0.321439475f}, {0.944806039f, -0.327630192f},
{0.942641497f, -0.333806872f}, {0.940436542f, -0.339969248f},
{0.938191354f, -0.346117049f}, {0.935905933f, -0.352250040f},
{0.933580399f, -0.358367950f}, {0.931214929f, -0.364470512f},
{0.928809524f, -0.370557427f}, {0.926364362f, -0.376628488f},
{0.923879504f, -0.382683426f}, {0.921355128f, -0.388721973f},
{0.918791234f, -0.394743860f}, {0.916187942f, -0.400748819f},
{0.913545430f, -0.406736642f}, {0.910863817f, -0.412707031f},
{0.908143163f, -0.418659747f}, {0.905383646f, -0.424594522f},
{0.902585268f, -0.430511087f}, {0.899748266f, -0.436409235f},
{0.896872759f, -0.442288697f}, {0.893958807f, -0.448149204f},
{0.891006529f, -0.453990489f}, {0.888016105f, -0.459812373f},
{0.884987652f, -0.465614527f}, {0.881921291f, -0.471396744f},
{0.878817141f, -0.477158755f}, {0.875675321f, -0.482900351f},
{0.872496009f, -0.488621235f}, {0.869279325f, -0.494321197f},
{0.866025388f, -0.500000000f}, {0.862734377f, -0.505657375f},
{0.859406412f, -0.511293113f}, {0.856041610f, -0.516906917f},
{0.852640152f, -0.522498548f}, {0.849202156f, -0.528067827f},
{0.845727801f, -0.533614516f}, {0.842217207f, -0.539138317f},
{0.838670552f, -0.544639051f}, {0.835087955f, -0.550116420f},
{0.831469595f, -0.555570245f}, {0.827815652f, -0.561000228f},
{0.824126184f, -0.566406250f}, {0.820401430f, -0.571787953f},
{0.816641569f, -0.577145219f}, {0.812846661f, -0.582477689f},
{0.809017003f, -0.587785244f}, {0.805152655f, -0.593067646f},
{0.801253796f, -0.598324597f}, {0.797320664f, -0.603555918f},
{0.793353319f, -0.608761430f}, {0.789352059f, -0.613940835f},
{0.785316944f, -0.619093955f}, {0.781248152f, -0.624220550f},
{0.777145982f, -0.629320383f}, {0.773010433f, -0.634393275f},
{0.768841803f, -0.639438987f}, {0.764640272f, -0.644457340f},
{0.760405958f, -0.649448037f}, {0.756139100f, -0.654410958f},
{0.751839817f, -0.659345806f}, {0.747508347f, -0.664252460f},
{0.743144810f, -0.669130623f}, {0.738749504f, -0.673980117f},
{0.734322488f, -0.678800762f}, {0.729864061f, -0.683592319f},
{0.725374401f, -0.688354552f}, {0.720853567f, -0.693087339f},
{0.716301918f, -0.697790444f}, {0.711719632f, -0.702463686f},
{0.707106769f, -0.707106769f}, {0.702463686f, -0.711719632f},
{0.697790444f, -0.716301918f}, {0.693087339f, -0.720853567f},
{0.688354552f, -0.725374401f}, {0.683592319f, -0.729864061f},
{0.678800762f, -0.734322488f}, {0.673980117f, -0.738749504f},
{0.669130623f, -0.743144810f}, {0.664252460f, -0.747508347f},
{0.659345806f, -0.751839817f}, {0.654410958f, -0.756139100f},
{0.649448037f, -0.760405958f}, {0.644457340f, -0.764640272f},
{0.639438987f, -0.768841803f}, {0.634393275f, -0.773010433f},
{0.629320383f, -0.777145982f}, {0.624220550f, -0.781248152f},
{0.619093955f, -0.785316944f}, {0.613940835f, -0.789352059f},
{0.608761430f, -0.793353319f}, {0.603555918f, -0.797320664f},
{0.598324597f, -0.801253796f}, {0.593067646f, -0.805152655f},
{0.587785244f, -0.809017003f}, {0.582477689f, -0.812846661f},
{0.577145219f, -0.816641569f}, {0.571787953f, -0.820401430f},
{0.566406250f, -0.824126184f}, {0.561000228f, -0.827815652f},
{0.555570245f, -0.831469595f}, {0.550116420f, -0.835087955f},
{0.544639051f, -0.838670552f}, {0.539138317f, -0.842217207f},
{0.533614516f, -0.845727801f}, {0.528067827f, -0.849202156f},
{0.522498548f, -0.852640152f}, {0.516906917f, -0.856041610f},
{0.511293113f, -0.859406412f}, {0.505657375f, -0.862734377f},
{0.500000000f, -0.866025388f}, {0.494321197f, -0.869279325f},
{0.488621235f, -0.872496009f}, {0.482900351f, -0.875675321f},
{0.477158755f, -0.878817141f}, {0.471396744f, -0.881921291f},
{0.465614527f, -0.884987652f}, {0.459812373f, -0.888016105f},
{0.453990489f, -0.891006529f}, {0.448149204f, -0.893958807f},
{0.442288697f, -0.896872759f}, {0.436409235f, -0.899748266f},
{0.430511087f, -0.902585268f}, {0.424594522f, -0.905383646f},
{0.418659747f, -0.908143163f}, {0.412707031f, -0.910863817f},
{0.406736642f, -0.913545430f}, {0.400748819f, -0.916187942f},
{0.394743860f, -0.918791234f}, {0.388721973f, -0.921355128f},
{0.382683426f, -0.923879504f}, {0.376628488f, -0.926364362f},
{0.370557427f, -0.928809524f}, {0.364470512f, -0.931214929f},
{0.358367950f, -0.933580399f}, {0.352250040f, -0.935905933f},
{0.346117049f, -0.938191354f}, {0.339969248f, -0.940436542f},
{0.333806872f, -0.942641497f}, {0.327630192f, -0.944806039f},
{0.321439475f, -0.946930110f}, {0.315234989f, -0.949013650f},
{0.309017003f, -0.951056540f}, {0.302785784f, -0.953058660f},
{0.296541572f, -0.955019951f}, {0.290284663f, -0.956940353f},
{0.284015357f, -0.958819747f}, {0.277733833f, -0.960658073f},
{0.271440446f, -0.962455213f}, {0.265135437f, -0.964211166f},
{0.258819044f, -0.965925813f}, {0.252491564f, -0.967599094f},
{0.246153295f, -0.969230890f}, {0.239804462f, -0.970821202f},
{0.233445361f, -0.972369909f}, {0.227076262f, -0.973876953f},
{0.220697433f, -0.975342333f}, {0.214309156f, -0.976765871f},
{0.207911685f, -0.978147626f}, {0.201505318f, -0.979487419f},
{0.195090324f, -0.980785251f}, {0.188666970f, -0.982041121f},
{0.182235524f, -0.983254910f}, {0.175796285f, -0.984426558f},
{0.169349506f, -0.985556066f}, {0.162895471f, -0.986643314f},
{0.156434461f, -0.987688363f}, {0.149966761f, -0.988691032f},
{0.143492624f, -0.989651382f}, {0.137012348f, -0.990569353f},
{0.130526185f, -0.991444886f}, {0.124034449f, -0.992277920f},
{0.117537394f, -0.993068457f}, {0.111035310f, -0.993816435f},
{0.104528464f, -0.994521916f}, {0.0980171412f, -0.995184720f},
{0.0915016159f, -0.995804906f}, {0.0849821791f, -0.996382475f},
{0.0784590989f, -0.996917307f}, {0.0719326511f, -0.997409463f},
{0.0654031262f, -0.997858942f}, {0.0588708036f, -0.998265624f},
{0.0523359552f, -0.998629510f}, {0.0457988679f, -0.998950660f},
{0.0392598175f, -0.999229014f}, {0.0327190831f, -0.999464571f},
{0.0261769481f, -0.999657333f}, {0.0196336918f, -0.999807239f},
{0.0130895954f, -0.999914348f}, {0.00654493785f, -0.999978602f},
{6.12323426e-17f, -1.00000000f}, {-0.00654493785f, -0.999978602f},
{-0.0130895954f, -0.999914348f}, {-0.0196336918f, -0.999807239f},
{-0.0261769481f, -0.999657333f}, {-0.0327190831f, -0.999464571f},
{-0.0392598175f, -0.999229014f}, {-0.0457988679f, -0.998950660f},
{-0.0523359552f, -0.998629510f}, {-0.0588708036f, -0.998265624f},
{-0.0654031262f, -0.997858942f}, {-0.0719326511f, -0.997409463f},
{-0.0784590989f, -0.996917307f}, {-0.0849821791f, -0.996382475f},
{-0.0915016159f, -0.995804906f}, {-0.0980171412f, -0.995184720f},
{-0.104528464f, -0.994521916f}, {-0.111035310f, -0.993816435f},
{-0.117537394f, -0.993068457f}, {-0.124034449f, -0.992277920f},
{-0.130526185f, -0.991444886f}, {-0.137012348f, -0.990569353f},
{-0.143492624f, -0.989651382f}, {-0.149966761f, -0.988691032f},
{-0.156434461f, -0.987688363f}, {-0.162895471f, -0.986643314f},
{-0.169349506f, -0.985556066f}, {-0.175796285f, -0.984426558f},
{-0.182235524f, -0.983254910f}, {-0.188666970f, -0.982041121f},
{-0.195090324f, -0.980785251f}, {-0.201505318f, -0.979487419f},
{-0.207911685f, -0.978147626f}, {-0.214309156f, -0.976765871f},
{-0.220697433f, -0.975342333f}, {-0.227076262f, -0.973876953f},
{-0.233445361f, -0.972369909f}, {-0.239804462f, -0.970821202f},
{-0.246153295f, -0.969230890f}, {-0.252491564f, -0.967599094f},
{-0.258819044f, -0.965925813f}, {-0.265135437f, -0.964211166f},
{-0.271440446f, -0.962455213f}, {-0.277733833f, -0.960658073f},
{-0.284015357f, -0.958819747f}, {-0.290284663f, -0.956940353f},
{-0.296541572f, -0.955019951f}, {-0.302785784f, -0.953058660f},
{-0.309017003f, -0.951056540f}, {-0.315234989f, -0.949013650f},
{-0.321439475f, -0.946930110f}, {-0.327630192f, -0.944806039f},
{-0.333806872f, -0.942641497f}, {-0.339969248f, -0.940436542f},
{-0.346117049f, -0.938191354f}, {-0.352250040f, -0.935905933f},
{-0.358367950f, -0.933580399f}, {-0.364470512f, -0.931214929f},
{-0.370557427f, -0.928809524f}, {-0.376628488f, -0.926364362f},
{-0.382683426f, -0.923879504f}, {-0.388721973f, -0.921355128f},
{-0.394743860f, -0.918791234f}, {-0.400748819f, -0.916187942f},
{-0.406736642f, -0.913545430f}, {-0.412707031f, -0.910863817f},
{-0.418659747f, -0.908143163f}, {-0.424594522f, -0.905383646f},
{-0.430511087f, -0.902585268f}, {-0.436409235f, -0.899748266f},
{-0.442288697f, -0.896872759f}, {-0.448149204f, -0.893958807f},
{-0.453990489f, -0.891006529f}, {-0.459812373f, -0.888016105f},
{-0.465614527f, -0.884987652f}, {-0.471396744f, -0.881921291f},
{-0.477158755f, -0.878817141f}, {-0.482900351f, -0.875675321f},
{-0.488621235f, -0.872496009f}, {-0.494321197f, -0.869279325f},
{-0.500000000f, -0.866025388f}, {-0.505657375f, -0.862734377f},
{-0.511293113f, -0.859406412f}, {-0.516906917f, -0.856041610f},
{-0.522498548f, -0.852640152f}, {-0.528067827f, -0.849202156f},
{-0.533614516f, -0.845727801f}, {-0.539138317f, -0.842217207f},
{-0.544639051f, -0.838670552f}, {-0.550116420f, -0.835087955f},
{-0.555570245f, -0.831469595f}, {-0.561000228f, -0.827815652f},
{-0.566406250f, -0.824126184f}, {-0.571787953f, -0.820401430f},
{-0.577145219f, -0.816641569f}, {-0.582477689f, -0.812846661f},
{-0.587785244f, -0.809017003f}, {-0.593067646f, -0.805152655f},
{-0.598324597f, -0.801253796f}, {-0.603555918f, -0.797320664f},
{-0.608761430f, -0.793353319f}, {-0.613940835f, -0.789352059f},
{-0.619093955f, -0.785316944f}, {-0.624220550f, -0.781248152f},
{-0.629320383f, -0.777145982f}, {-0.634393275f, -0.773010433f},
{-0.639438987f, -0.768841803f}, {-0.644457340f, -0.764640272f},
{-0.649448037f, -0.760405958f}, {-0.654410958f, -0.756139100f},
{-0.659345806f, -0.751839817f}, {-0.664252460f, -0.747508347f},
{-0.669130623f, -0.743144810f}, {-0.673980117f, -0.738749504f},
{-0.678800762f, -0.734322488f}, {-0.683592319f, -0.729864061f},
{-0.688354552f, -0.725374401f}, {-0.693087339f, -0.720853567f},
{-0.697790444f, -0.716301918f}, {-0.702463686f, -0.711719632f},
{-0.707106769f, -0.707106769f}, {-0.711719632f, -0.702463686f},
{-0.716301918f, -0.697790444f}, {-0.720853567f, -0.693087339f},
{-0.725374401f, -0.688354552f}, {-0.729864061f, -0.683592319f},
{-0.734322488f, -0.678800762f}, {-0.738749504f, -0.673980117f},
{-0.743144810f, -0.669130623f}, {-0.747508347f, -0.664252460f},
{-0.751839817f, -0.659345806f}, {-0.756139100f, -0.654410958f},
{-0.760405958f, -0.649448037f}, {-0.764640272f, -0.644457340f},
{-0.768841803f, -0.639438987f}, {-0.773010433f, -0.634393275f},
{-0.777145982f, -0.629320383f}, {-0.781248152f, -0.624220550f},
{-0.785316944f, -0.619093955f}, {-0.789352059f, -0.613940835f},
{-0.793353319f, -0.608761430f}, {-0.797320664f, -0.603555918f},
{-0.801253796f, -0.598324597f}, {-0.805152655f, -0.593067646f},
{-0.809017003f, -0.587785244f}, {-0.812846661f, -0.582477689f},
{-0.816641569f, -0.577145219f}, {-0.820401430f, -0.571787953f},
{-0.824126184f, -0.566406250f}, {-0.827815652f, -0.561000228f},
{-0.831469595f, -0.555570245f}, {-0.835087955f, -0.550116420f},
{-0.838670552f, -0.544639051f}, {-0.842217207f, -0.539138317f},
{-0.845727801f, -0.533614516f}, {-0.849202156f, -0.528067827f},
{-0.852640152f, -0.522498548f}, {-0.856041610f, -0.516906917f},
{-0.859406412f, -0.511293113f}, {-0.862734377f, -0.505657375f},
{-0.866025388f, -0.500000000f}, {-0.869279325f, -0.494321197f},
{-0.872496009f, -0.488621235f}, {-0.875675321f, -0.482900351f},
{-0.878817141f, -0.477158755f}, {-0.881921291f, -0.471396744f},
{-0.884987652f, -0.465614527f}, {-0.888016105f, -0.459812373f},
{-0.891006529f, -0.453990489f}, {-0.893958807f, -0.448149204f},
{-0.896872759f, -0.442288697f}, {-0.899748266f, -0.436409235f},
{-0.902585268f, -0.430511087f}, {-0.905383646f, -0.424594522f},
{-0.908143163f, -0.418659747f}, {-0.910863817f, -0.412707031f},
{-0.913545430f, -0.406736642f}, {-0.916187942f, -0.400748819f},
{-0.918791234f, -0.394743860f}, {-0.921355128f, -0.388721973f},
{-0.923879504f, -0.382683426f}, {-0.926364362f, -0.376628488f},
{-0.928809524f, -0.370557427f}, {-0.931214929f, -0.364470512f},
{-0.933580399f, -0.358367950f}, {-0.935905933f, -0.352250040f},
{-0.938191354f, -0.346117049f}, {-0.940436542f, -0.339969248f},
{-0.942641497f, -0.333806872f}, {-0.944806039f, -0.327630192f},
{-0.946930110f, -0.321439475f}, {-0.949013650f, -0.315234989f},
{-0.951056540f, -0.309017003f}, {-0.953058660f, -0.302785784f},
{-0.955019951f, -0.296541572f}, {-0.956940353f, -0.290284663f},
{-0.958819747f, -0.284015357f}, {-0.960658073f, -0.277733833f},
{-0.962455213f, -0.271440446f}, {-0.964211166f, -0.265135437f},
{-0.965925813f, -0.258819044f}, {-0.967599094f, -0.252491564f},
{-0.969230890f, -0.246153295f}, {-0.970821202f, -0.239804462f},
{-0.972369909f, -0.233445361f}, {-0.973876953f, -0.227076262f},
{-0.975342333f, -0.220697433f}, {-0.976765871f, -0.214309156f},
{-0.978147626f, -0.207911685f}, {-0.979487419f, -0.201505318f},
{-0.980785251f, -0.195090324f}, {-0.982041121f, -0.188666970f},
{-0.983254910f, -0.182235524f}, {-0.984426558f, -0.175796285f},
{-0.985556066f, -0.169349506f}, {-0.986643314f, -0.162895471f},
{-0.987688363f, -0.156434461f}, {-0.988691032f, -0.149966761f},
{-0.989651382f, -0.143492624f}, {-0.990569353f, -0.137012348f},
{-0.991444886f, -0.130526185f}, {-0.992277920f, -0.124034449f},
{-0.993068457f, -0.117537394f}, {-0.993816435f, -0.111035310f},
{-0.994521916f, -0.104528464f}, {-0.995184720f, -0.0980171412f},
{-0.995804906f, -0.0915016159f}, {-0.996382475f, -0.0849821791f},
{-0.996917307f, -0.0784590989f}, {-0.997409463f, -0.0719326511f},
{-0.997858942f, -0.0654031262f}, {-0.998265624f, -0.0588708036f},
{-0.998629510f, -0.0523359552f}, {-0.998950660f, -0.0457988679f},
{-0.999229014f, -0.0392598175f}, {-0.999464571f, -0.0327190831f},
{-0.999657333f, -0.0261769481f}, {-0.999807239f, -0.0196336918f},
{-0.999914348f, -0.0130895954f}, {-0.999978602f, -0.00654493785f},
{-1.00000000f, -1.22464685e-16f}, {-0.999978602f, 0.00654493785f},
{-0.999914348f, 0.0130895954f}, {-0.999807239f, 0.0196336918f},
{-0.999657333f, 0.0261769481f}, {-0.999464571f, 0.0327190831f},
{-0.999229014f, 0.0392598175f}, {-0.998950660f, 0.0457988679f},
{-0.998629510f, 0.0523359552f}, {-0.998265624f, 0.0588708036f},
{-0.997858942f, 0.0654031262f}, {-0.997409463f, 0.0719326511f},
{-0.996917307f, 0.0784590989f}, {-0.996382475f, 0.0849821791f},
{-0.995804906f, 0.0915016159f}, {-0.995184720f, 0.0980171412f},
{-0.994521916f, 0.104528464f}, {-0.993816435f, 0.111035310f},
{-0.993068457f, 0.117537394f}, {-0.992277920f, 0.124034449f},
{-0.991444886f, 0.130526185f}, {-0.990569353f, 0.137012348f},
{-0.989651382f, 0.143492624f}, {-0.988691032f, 0.149966761f},
{-0.987688363f, 0.156434461f}, {-0.986643314f, 0.162895471f},
{-0.985556066f, 0.169349506f}, {-0.984426558f, 0.175796285f},
{-0.983254910f, 0.182235524f}, {-0.982041121f, 0.188666970f},
{-0.980785251f, 0.195090324f}, {-0.979487419f, 0.201505318f},
{-0.978147626f, 0.207911685f}, {-0.976765871f, 0.214309156f},
{-0.975342333f, 0.220697433f}, {-0.973876953f, 0.227076262f},
{-0.972369909f, 0.233445361f}, {-0.970821202f, 0.239804462f},
{-0.969230890f, 0.246153295f}, {-0.967599094f, 0.252491564f},
{-0.965925813f, 0.258819044f}, {-0.964211166f, 0.265135437f},
{-0.962455213f, 0.271440446f}, {-0.960658073f, 0.277733833f},
{-0.958819747f, 0.284015357f}, {-0.956940353f, 0.290284663f},
{-0.955019951f, 0.296541572f}, {-0.953058660f, 0.302785784f},
{-0.951056540f, 0.309017003f}, {-0.949013650f, 0.315234989f},
{-0.946930110f, 0.321439475f}, {-0.944806039f, 0.327630192f},
{-0.942641497f, 0.333806872f}, {-0.940436542f, 0.339969248f},
{-0.938191354f, 0.346117049f}, {-0.935905933f, 0.352250040f},
{-0.933580399f, 0.358367950f}, {-0.931214929f, 0.364470512f},
{-0.928809524f, 0.370557427f}, {-0.926364362f, 0.376628488f},
{-0.923879504f, 0.382683426f}, {-0.921355128f, 0.388721973f},
{-0.918791234f, 0.394743860f}, {-0.916187942f, 0.400748819f},
{-0.913545430f, 0.406736642f}, {-0.910863817f, 0.412707031f},
{-0.908143163f, 0.418659747f}, {-0.905383646f, 0.424594522f},
{-0.902585268f, 0.430511087f}, {-0.899748266f, 0.436409235f},
{-0.896872759f, 0.442288697f}, {-0.893958807f, 0.448149204f},
{-0.891006529f, 0.453990489f}, {-0.888016105f, 0.459812373f},
{-0.884987652f, 0.465614527f}, {-0.881921291f, 0.471396744f},
{-0.878817141f, 0.477158755f}, {-0.875675321f, 0.482900351f},
{-0.872496009f, 0.488621235f}, {-0.869279325f, 0.494321197f},
{-0.866025388f, 0.500000000f}, {-0.862734377f, 0.505657375f},
{-0.859406412f, 0.511293113f}, {-0.856041610f, 0.516906917f},
{-0.852640152f, 0.522498548f}, {-0.849202156f, 0.528067827f},
{-0.845727801f, 0.533614516f}, {-0.842217207f, 0.539138317f},
{-0.838670552f, 0.544639051f}, {-0.835087955f, 0.550116420f},
{-0.831469595f, 0.555570245f}, {-0.827815652f, 0.561000228f},
{-0.824126184f, 0.566406250f}, {-0.820401430f, 0.571787953f},
{-0.816641569f, 0.577145219f}, {-0.812846661f, 0.582477689f},
{-0.809017003f, 0.587785244f}, {-0.805152655f, 0.593067646f},
{-0.801253796f, 0.598324597f}, {-0.797320664f, 0.603555918f},
{-0.793353319f, 0.608761430f}, {-0.789352059f, 0.613940835f},
{-0.785316944f, 0.619093955f}, {-0.781248152f, 0.624220550f},
{-0.777145982f, 0.629320383f}, {-0.773010433f, 0.634393275f},
{-0.768841803f, 0.639438987f}, {-0.764640272f, 0.644457340f},
{-0.760405958f, 0.649448037f}, {-0.756139100f, 0.654410958f},
{-0.751839817f, 0.659345806f}, {-0.747508347f, 0.664252460f},
{-0.743144810f, 0.669130623f}, {-0.738749504f, 0.673980117f},
{-0.734322488f, 0.678800762f}, {-0.729864061f, 0.683592319f},
{-0.725374401f, 0.688354552f}, {-0.720853567f, 0.693087339f},
{-0.716301918f, 0.697790444f}, {-0.711719632f, 0.702463686f},
{-0.707106769f, 0.707106769f}, {-0.702463686f, 0.711719632f},
{-0.697790444f, 0.716301918f}, {-0.693087339f, 0.720853567f},
{-0.688354552f, 0.725374401f}, {-0.683592319f, 0.729864061f},
{-0.678800762f, 0.734322488f}, {-0.673980117f, 0.738749504f},
{-0.669130623f, 0.743144810f}, {-0.664252460f, 0.747508347f},
{-0.659345806f, 0.751839817f}, {-0.654410958f, 0.756139100f},
{-0.649448037f, 0.760405958f}, {-0.644457340f, 0.764640272f},
{-0.639438987f, 0.768841803f}, {-0.634393275f, 0.773010433f},
{-0.629320383f, 0.777145982f}, {-0.624220550f, 0.781248152f},
{-0.619093955f, 0.785316944f}, {-0.613940835f, 0.789352059f},
{-0.608761430f, 0.793353319f}, {-0.603555918f, 0.797320664f},
{-0.598324597f, 0.801253796f}, {-0.593067646f, 0.805152655f},
{-0.587785244f, 0.809017003f}, {-0.582477689f, 0.812846661f},
{-0.577145219f, 0.816641569f}, {-0.571787953f, 0.820401430f},
{-0.566406250f, 0.824126184f}, {-0.561000228f, 0.827815652f},
{-0.555570245f, 0.831469595f}, {-0.550116420f, 0.835087955f},
{-0.544639051f, 0.838670552f}, {-0.539138317f, 0.842217207f},
{-0.533614516f, 0.845727801f}, {-0.528067827f, 0.849202156f},
{-0.522498548f, 0.852640152f}, {-0.516906917f, 0.856041610f},
{-0.511293113f, 0.859406412f}, {-0.505657375f, 0.862734377f},
{-0.500000000f, 0.866025388f}, {-0.494321197f, 0.869279325f},
{-0.488621235f, 0.872496009f}, {-0.482900351f, 0.875675321f},
{-0.477158755f, 0.878817141f}, {-0.471396744f, 0.881921291f},
{-0.465614527f, 0.884987652f}, {-0.459812373f, 0.888016105f},
{-0.453990489f, 0.891006529f}, {-0.448149204f, 0.893958807f},
{-0.442288697f, 0.896872759f}, {-0.436409235f, 0.899748266f},
{-0.430511087f, 0.902585268f}, {-0.424594522f, 0.905383646f},
{-0.418659747f, 0.908143163f}, {-0.412707031f, 0.910863817f},
{-0.406736642f, 0.913545430f}, {-0.400748819f, 0.916187942f},
{-0.394743860f, 0.918791234f}, {-0.388721973f, 0.921355128f},
{-0.382683426f, 0.923879504f}, {-0.376628488f, 0.926364362f},
{-0.370557427f, 0.928809524f}, {-0.364470512f, 0.931214929f},
{-0.358367950f, 0.933580399f}, {-0.352250040f, 0.935905933f},
{-0.346117049f, 0.938191354f}, {-0.339969248f, 0.940436542f},
{-0.333806872f, 0.942641497f}, {-0.327630192f, 0.944806039f},
{-0.321439475f, 0.946930110f}, {-0.315234989f, 0.949013650f},
{-0.309017003f, 0.951056540f}, {-0.302785784f, 0.953058660f},
{-0.296541572f, 0.955019951f}, {-0.290284663f, 0.956940353f},
{-0.284015357f, 0.958819747f}, {-0.277733833f, 0.960658073f},
{-0.271440446f, 0.962455213f}, {-0.265135437f, 0.964211166f},
{-0.258819044f, 0.965925813f}, {-0.252491564f, 0.967599094f},
{-0.246153295f, 0.969230890f}, {-0.239804462f, 0.970821202f},
{-0.233445361f, 0.972369909f}, {-0.227076262f, 0.973876953f},
{-0.220697433f, 0.975342333f}, {-0.214309156f, 0.976765871f},
{-0.207911685f, 0.978147626f}, {-0.201505318f, 0.979487419f},
{-0.195090324f, 0.980785251f}, {-0.188666970f, 0.982041121f},
{-0.182235524f, 0.983254910f}, {-0.175796285f, 0.984426558f},
{-0.169349506f, 0.985556066f}, {-0.162895471f, 0.986643314f},
{-0.156434461f, 0.987688363f}, {-0.149966761f, 0.988691032f},
{-0.143492624f, 0.989651382f}, {-0.137012348f, 0.990569353f},
{-0.130526185f, 0.991444886f}, {-0.124034449f, 0.992277920f},
{-0.117537394f, 0.993068457f}, {-0.111035310f, 0.993816435f},
{-0.104528464f, 0.994521916f}, {-0.0980171412f, 0.995184720f},
{-0.0915016159f, 0.995804906f}, {-0.0849821791f, 0.996382475f},
{-0.0784590989f, 0.996917307f}, {-0.0719326511f, 0.997409463f},
{-0.0654031262f, 0.997858942f}, {-0.0588708036f, 0.998265624f},
{-0.0523359552f, 0.998629510f}, {-0.0457988679f, 0.998950660f},
{-0.0392598175f, 0.999229014f}, {-0.0327190831f, 0.999464571f},
{-0.0261769481f, 0.999657333f}, {-0.0196336918f, 0.999807239f},
{-0.0130895954f, 0.999914348f}, {-0.00654493785f, 0.999978602f},
{-1.83697015e-16f, 1.00000000f}, {0.00654493785f, 0.999978602f},
{0.0130895954f, 0.999914348f}, {0.0196336918f, 0.999807239f},
{0.0261769481f, 0.999657333f}, {0.0327190831f, 0.999464571f},
{0.0392598175f, 0.999229014f}, {0.0457988679f, 0.998950660f},
{0.0523359552f, 0.998629510f}, {0.0588708036f, 0.998265624f},
{0.0654031262f, 0.997858942f}, {0.0719326511f, 0.997409463f},
{0.0784590989f, 0.996917307f}, {0.0849821791f, 0.996382475f},
{0.0915016159f, 0.995804906f}, {0.0980171412f, 0.995184720f},
{0.104528464f, 0.994521916f}, {0.111035310f, 0.993816435f},
{0.117537394f, 0.993068457f}, {0.124034449f, 0.992277920f},
{0.130526185f, 0.991444886f}, {0.137012348f, 0.990569353f},
{0.143492624f, 0.989651382f}, {0.149966761f, 0.988691032f},
{0.156434461f, 0.987688363f}, {0.162895471f, 0.986643314f},
{0.169349506f, 0.985556066f}, {0.175796285f, 0.984426558f},
{0.182235524f, 0.983254910f}, {0.188666970f, 0.982041121f},
{0.195090324f, 0.980785251f}, {0.201505318f, 0.979487419f},
{0.207911685f, 0.978147626f}, {0.214309156f, 0.976765871f},
{0.220697433f, 0.975342333f}, {0.227076262f, 0.973876953f},
{0.233445361f, 0.972369909f}, {0.239804462f, 0.970821202f},
{0.246153295f, 0.969230890f}, {0.252491564f, 0.967599094f},
{0.258819044f, 0.965925813f}, {0.265135437f, 0.964211166f},
{0.271440446f, 0.962455213f}, {0.277733833f, 0.960658073f},
{0.284015357f, 0.958819747f}, {0.290284663f, 0.956940353f},
{0.296541572f, 0.955019951f}, {0.302785784f, 0.953058660f},
{0.309017003f, 0.951056540f}, {0.315234989f, 0.949013650f},
{0.321439475f, 0.946930110f}, {0.327630192f, 0.944806039f},
{0.333806872f, 0.942641497f}, {0.339969248f, 0.940436542f},
{0.346117049f, 0.938191354f}, {0.352250040f, 0.935905933f},
{0.358367950f, 0.933580399f}, {0.364470512f, 0.931214929f},
{0.370557427f, 0.928809524f}, {0.376628488f, 0.926364362f},
{0.382683426f, 0.923879504f}, {0.388721973f, 0.921355128f},
{0.394743860f, 0.918791234f}, {0.400748819f, 0.916187942f},
{0.406736642f, 0.913545430f}, {0.412707031f, 0.910863817f},
{0.418659747f, 0.908143163f}, {0.424594522f, 0.905383646f},
{0.430511087f, 0.902585268f}, {0.436409235f, 0.899748266f},
{0.442288697f, 0.896872759f}, {0.448149204f, 0.893958807f},
{0.453990489f, 0.891006529f}, {0.459812373f, 0.888016105f},
{0.465614527f, 0.884987652f}, {0.471396744f, 0.881921291f},
{0.477158755f, 0.878817141f}, {0.482900351f, 0.875675321f},
{0.488621235f, 0.872496009f}, {0.494321197f, 0.869279325f},
{0.500000000f, 0.866025388f}, {0.505657375f, 0.862734377f},
{0.511293113f, 0.859406412f}, {0.516906917f, 0.856041610f},
{0.522498548f, 0.852640152f}, {0.528067827f, 0.849202156f},
{0.533614516f, 0.845727801f}, {0.539138317f, 0.842217207f},
{0.544639051f, 0.838670552f}, {0.550116420f, 0.835087955f},
{0.555570245f, 0.831469595f}, {0.561000228f, 0.827815652f},
{0.566406250f, 0.824126184f}, {0.571787953f, 0.820401430f},
{0.577145219f, 0.816641569f}, {0.582477689f, 0.812846661f},
{0.587785244f, 0.809017003f}, {0.593067646f, 0.805152655f},
{0.598324597f, 0.801253796f}, {0.603555918f, 0.797320664f},
{0.608761430f, 0.793353319f}, {0.613940835f, 0.789352059f},
{0.619093955f, 0.785316944f}, {0.624220550f, 0.781248152f},
{0.629320383f, 0.777145982f}, {0.634393275f, 0.773010433f},
{0.639438987f, 0.768841803f}, {0.644457340f, 0.764640272f},
{0.649448037f, 0.760405958f}, {0.654410958f, 0.756139100f},
{0.659345806f, 0.751839817f}, {0.664252460f, 0.747508347f},
{0.669130623f, 0.743144810f}, {0.673980117f, 0.738749504f},
{0.678800762f, 0.734322488f}, {0.683592319f, 0.729864061f},
{0.688354552f, 0.725374401f}, {0.693087339f, 0.720853567f},
{0.697790444f, 0.716301918f}, {0.702463686f, 0.711719632f},
{0.707106769f, 0.707106769f}, {0.711719632f, 0.702463686f},
{0.716301918f, 0.697790444f}, {0.720853567f, 0.693087339f},
{0.725374401f, 0.688354552f}, {0.729864061f, 0.683592319f},
{0.734322488f, 0.678800762f}, {0.738749504f, 0.673980117f},
{0.743144810f, 0.669130623f}, {0.747508347f, 0.664252460f},
{0.751839817f, 0.659345806f}, {0.756139100f, 0.654410958f},
{0.760405958f, 0.649448037f}, {0.764640272f, 0.644457340f},
{0.768841803f, 0.639438987f}, {0.773010433f, 0.634393275f},
{0.777145982f, 0.629320383f}, {0.781248152f, 0.624220550f},
{0.785316944f, 0.619093955f}, {0.789352059f, 0.613940835f},
{0.793353319f, 0.608761430f}, {0.797320664f, 0.603555918f},
{0.801253796f, 0.598324597f}, {0.805152655f, 0.593067646f},
{0.809017003f, 0.587785244f}, {0.812846661f, 0.582477689f},
{0.816641569f, 0.577145219f}, {0.820401430f, 0.571787953f},
{0.824126184f, 0.566406250f}, {0.827815652f, 0.561000228f},
{0.831469595f, 0.555570245f}, {0.835087955f, 0.550116420f},
{0.838670552f, 0.544639051f}, {0.842217207f, 0.539138317f},
{0.845727801f, 0.533614516f}, {0.849202156f, 0.528067827f},
{0.852640152f, 0.522498548f}, {0.856041610f, 0.516906917f},
{0.859406412f, 0.511293113f}, {0.862734377f, 0.505657375f},
{0.866025388f, 0.500000000f}, {0.869279325f, 0.494321197f},
{0.872496009f, 0.488621235f}, {0.875675321f, 0.482900351f},
{0.878817141f, 0.477158755f}, {0.881921291f, 0.471396744f},
{0.884987652f, 0.465614527f}, {0.888016105f, 0.459812373f},
{0.891006529f, 0.453990489f}, {0.893958807f, 0.448149204f},
{0.896872759f, 0.442288697f}, {0.899748266f, 0.436409235f},
{0.902585268f, 0.430511087f}, {0.905383646f, 0.424594522f},
{0.908143163f, 0.418659747f}, {0.910863817f, 0.412707031f},
{0.913545430f, 0.406736642f}, {0.916187942f, 0.400748819f},
{0.918791234f, 0.394743860f}, {0.921355128f, 0.388721973f},
{0.923879504f, 0.382683426f}, {0.926364362f, 0.376628488f},
{0.928809524f, 0.370557427f}, {0.931214929f, 0.364470512f},
{0.933580399f, 0.358367950f}, {0.935905933f, 0.352250040f},
{0.938191354f, 0.346117049f}, {0.940436542f, 0.339969248f},
{0.942641497f, 0.333806872f}, {0.944806039f, 0.327630192f},
{0.946930110f, 0.321439475f}, {0.949013650f, 0.315234989f},
{0.951056540f, 0.309017003f}, {0.953058660f, 0.302785784f},
{0.955019951f, 0.296541572f}, {0.956940353f, 0.290284663f},
{0.958819747f, 0.284015357f}, {0.960658073f, 0.277733833f},
{0.962455213f, 0.271440446f}, {0.964211166f, 0.265135437f},
{0.965925813f, 0.258819044f}, {0.967599094f, 0.252491564f},
{0.969230890f, 0.246153295f}, {0.970821202f, 0.239804462f},
{0.972369909f, 0.233445361f}, {0.973876953f, 0.227076262f},
{0.975342333f, 0.220697433f}, {0.976765871f, 0.214309156f},
{0.978147626f, 0.207911685f}, {0.979487419f, 0.201505318f},
{0.980785251f, 0.195090324f}, {0.982041121f, 0.188666970f},
{0.983254910f, 0.182235524f}, {0.984426558f, 0.175796285f},
{0.985556066f, 0.169349506f}, {0.986643314f, 0.162895471f},
{0.987688363f, 0.156434461f}, {0.988691032f, 0.149966761f},
{0.989651382f, 0.143492624f}, {0.990569353f, 0.137012348f},
{0.991444886f, 0.130526185f}, {0.992277920f, 0.124034449f},
{0.993068457f, 0.117537394f}, {0.993816435f, 0.111035310f},
{0.994521916f, 0.104528464f}, {0.995184720f, 0.0980171412f},
{0.995804906f, 0.0915016159f}, {0.996382475f, 0.0849821791f},
{0.996917307f, 0.0784590989f}, {0.997409463f, 0.0719326511f},
{0.997858942f, 0.0654031262f}, {0.998265624f, 0.0588708036f},
{0.998629510f, 0.0523359552f}, {0.998950660f, 0.0457988679f},
{0.999229014f, 0.0392598175f}, {0.999464571f, 0.0327190831f},
{0.999657333f, 0.0261769481f}, {0.999807239f, 0.0196336918f},
{0.999914348f, 0.0130895954f}, {0.999978602f, 0.00654493785f},
};
const kiss_fft_state rnn_kfft = {
960, /* nfft */
0.0010416667f, /* scale */
-1, /* shift */
{5, 192, 3, 64, 4, 16, 4, 4, 4, 1, 0, 0, 0, 0, 0, 0, }, /* factors */
fft_bitrev, /* bitrev*/
fft_twiddles, /* twiddles*/
(arch_fft_state *)&arch_fft, /* arch_fft*/
};
const float rnn_half_window[] = {
4.20549168e-06f, 3.78491532e-05f, 0.000105135041f, 0.000206060256f, 0.000340620492f,
0.000508809986f, 0.000710621476f, 0.000946046319f, 0.00121507444f, 0.00151769421f,
0.00185389258f, 0.00222365512f, 0.00262696599f, 0.00306380726f, 0.00353416055f,
0.00403800514f, 0.00457531959f, 0.00514607970f, 0.00575026125f, 0.00638783723f,
0.00705878017f, 0.00776306028f, 0.00850064680f, 0.00927150715f, 0.0100756064f,
0.0109129101f, 0.0117833801f, 0.0126869772f, 0.0136236614f, 0.0145933898f,
0.0155961197f, 0.0166318044f, 0.0177003983f, 0.0188018531f, 0.0199361145f,
0.0211031344f, 0.0223028567f, 0.0235352255f, 0.0248001851f, 0.0260976739f,
0.0274276342f, 0.0287899990f, 0.0301847085f, 0.0316116922f, 0.0330708846f,
0.0345622115f, 0.0360856056f, 0.0376409888f, 0.0392282903f, 0.0408474281f,
0.0424983241f, 0.0441808924f, 0.0458950549f, 0.0476407260f, 0.0494178124f,
0.0512262285f, 0.0530658774f, 0.0549366735f, 0.0568385124f, 0.0587713011f,
0.0607349351f, 0.0627293140f, 0.0647543296f, 0.0668098852f, 0.0688958541f,
0.0710121393f, 0.0731586292f, 0.0753351897f, 0.0775417164f, 0.0797780901f,
0.0820441842f, 0.0843398646f, 0.0866650119f, 0.0890194997f, 0.0914031938f,
0.0938159525f, 0.0962576419f, 0.0987281203f, 0.101227246f, 0.103754878f,
0.106310867f, 0.108895063f, 0.111507311f, 0.114147455f, 0.116815343f,
0.119510807f, 0.122233689f, 0.124983832f, 0.127761051f, 0.130565181f,
0.133396059f, 0.136253506f, 0.139137328f, 0.142047361f, 0.144983411f,
0.147945285f, 0.150932819f, 0.153945804f, 0.156984031f, 0.160047337f,
0.163135484f, 0.166248307f, 0.169385567f, 0.172547072f, 0.175732598f,
0.178941950f, 0.182174906f, 0.185431242f, 0.188710734f, 0.192013159f,
0.195338294f, 0.198685899f, 0.202055752f, 0.205447599f, 0.208861232f,
0.212296382f, 0.215752810f, 0.219230279f, 0.222728521f, 0.226247311f,
0.229786381f, 0.233345464f, 0.236924306f, 0.240522653f, 0.244140238f,
0.247776777f, 0.251432031f, 0.255105674f, 0.258797467f, 0.262507141f,
0.266234398f, 0.269978970f, 0.273740560f, 0.277518868f, 0.281313598f,
0.285124481f, 0.288951218f, 0.292793512f, 0.296651065f, 0.300523549f,
0.304410696f, 0.308312178f, 0.312227666f, 0.316156894f, 0.320099503f,
0.324055225f, 0.328023702f, 0.332004637f, 0.335997701f, 0.340002567f,
0.344018906f, 0.348046392f, 0.352084726f, 0.356133521f, 0.360192508f,
0.364261299f, 0.368339598f, 0.372427016f, 0.376523286f, 0.380627990f,
0.384740859f, 0.388861477f, 0.392989576f, 0.397124738f, 0.401266664f,
0.405414969f, 0.409569323f, 0.413729399f, 0.417894781f, 0.422065198f,
0.426240236f, 0.430419534f, 0.434602767f, 0.438789606f, 0.442979604f,
0.447172493f, 0.451367885f, 0.455565393f, 0.459764689f, 0.463965416f,
0.468167186f, 0.472369671f, 0.476572484f, 0.480775267f, 0.484977663f,
0.489179343f, 0.493379891f, 0.497579008f, 0.501776278f, 0.505971372f,
0.510163903f, 0.514353573f, 0.518539906f, 0.522722721f, 0.526901484f,
0.531075954f, 0.535245717f, 0.539410412f, 0.543569744f, 0.547723293f,
0.551870763f, 0.556011736f, 0.560145974f, 0.564273000f, 0.568392515f,
0.572504222f, 0.576607704f, 0.580702662f, 0.584788740f, 0.588865638f,
0.592932940f, 0.596990347f, 0.601037502f, 0.605074167f, 0.609099925f,
0.613114417f, 0.617117405f, 0.621108532f, 0.625087440f, 0.629053831f,
0.633007407f, 0.636947870f, 0.640874863f, 0.644788086f, 0.648687243f,
0.652572036f, 0.656442165f, 0.660297334f, 0.664137185f, 0.667961538f,
0.671769977f, 0.675562322f, 0.679338276f, 0.683097482f, 0.686839759f,
0.690564752f, 0.694272280f, 0.697961986f, 0.701633692f, 0.705287039f,
0.708921850f, 0.712537885f, 0.716134787f, 0.719712436f, 0.723270535f,
0.726808906f, 0.730327189f, 0.733825266f, 0.737302899f, 0.740759790f,
0.744195819f, 0.747610688f, 0.751004279f, 0.754376352f, 0.757726669f,
0.761055112f, 0.764361382f, 0.767645359f, 0.770906866f, 0.774145722f,
0.777361751f, 0.780554771f, 0.783724606f, 0.786871076f, 0.789994121f,
0.793093503f, 0.796169102f, 0.799220800f, 0.802248418f, 0.805251837f,
0.808230937f, 0.811185598f, 0.814115703f, 0.817021132f, 0.819901764f,
0.822757542f, 0.825588286f, 0.828393936f, 0.831174433f, 0.833929658f,
0.836659551f, 0.839363992f, 0.842042983f, 0.844696403f, 0.847324252f,
0.849926353f, 0.852502763f, 0.855053425f, 0.857578218f, 0.860077202f,
0.862550259f, 0.864997447f, 0.867418647f, 0.869813919f, 0.872183204f,
0.874526560f, 0.876843870f, 0.879135191f, 0.881400526f, 0.883639932f,
0.885853291f, 0.888040781f, 0.890202343f, 0.892337978f, 0.894447744f,
0.896531701f, 0.898589849f, 0.900622249f, 0.902628958f, 0.904610038f,
0.906565487f, 0.908495426f, 0.910399914f, 0.912279010f, 0.914132774f,
0.915961266f, 0.917764664f, 0.919542909f, 0.921296239f, 0.923024654f,
0.924728215f, 0.926407158f, 0.928061485f, 0.929691315f, 0.931296766f,
0.932878017f, 0.934435070f, 0.935968161f, 0.937477291f, 0.938962698f,
0.940424502f, 0.941862822f, 0.943277776f, 0.944669485f, 0.946038187f,
0.947383940f, 0.948706925f, 0.950007319f, 0.951285243f, 0.952540874f,
0.953774393f, 0.954985917f, 0.956175685f, 0.957343817f, 0.958490491f,
0.959615886f, 0.960720181f, 0.961803555f, 0.962866247f, 0.963908315f,
0.964930058f, 0.965931594f, 0.966913164f, 0.967874944f, 0.968817174f,
0.969739914f, 0.970643520f, 0.971528113f, 0.972393870f, 0.973241091f,
0.974069893f, 0.974880517f, 0.975673139f, 0.976447999f, 0.977205336f,
0.977945268f, 0.978668094f, 0.979374051f, 0.980063200f, 0.980735898f,
0.981392324f, 0.982032716f, 0.982657254f, 0.983266115f, 0.983859658f,
0.984437943f, 0.985001266f, 0.985549867f, 0.986083925f, 0.986603677f,
0.987109363f, 0.987601161f, 0.988079309f, 0.988544047f, 0.988995552f,
0.989434063f, 0.989859879f, 0.990273118f, 0.990674019f, 0.991062820f,
0.991439700f, 0.991804957f, 0.992158771f, 0.992501318f, 0.992832899f,
0.993153632f, 0.993463814f, 0.993763626f, 0.994053245f, 0.994332969f,
0.994602919f, 0.994863331f, 0.995114446f, 0.995356441f, 0.995589554f,
0.995813966f, 0.996029854f, 0.996237516f, 0.996437073f, 0.996628702f,
0.996812642f, 0.996989131f, 0.997158289f, 0.997320294f, 0.997475445f,
0.997623861f, 0.997765720f, 0.997901261f, 0.998030603f, 0.998153925f,
0.998271465f, 0.998383403f, 0.998489857f, 0.998591006f, 0.998687088f,
0.998778164f, 0.998864532f, 0.998946249f, 0.999023557f, 0.999096513f,
0.999165416f, 0.999230266f, 0.999291301f, 0.999348700f, 0.999402523f,
0.999453008f, 0.999500215f, 0.999544322f, 0.999585509f, 0.999623775f,
0.999659419f, 0.999692440f, 0.999723017f, 0.999751270f, 0.999777317f,
0.999801278f, 0.999823213f, 0.999843359f, 0.999861658f, 0.999878347f,
0.999893486f, 0.999907196f, 0.999919534f, 0.999930561f, 0.999940455f,
0.999949217f, 0.999957025f, 0.999963880f, 0.999969840f, 0.999975085f,
0.999979615f, 0.999983490f, 0.999986768f, 0.999989510f, 0.999991834f,
0.999993742f, 0.999995291f, 0.999996543f, 0.999997556f, 0.999998271f,
0.999998868f, 0.999999285f, 0.999999523f, 0.999999762f, 0.999999881f,
0.999999940f, 1.00000000f, 1.00000000f, 1.00000000f, 1.00000000f,
};
const float rnn_dct_table[] = {
0.707106769f, 0.998795450f, 0.995184720f, 0.989176512f, 0.980785251f,
0.970031261f, 0.956940353f, 0.941544056f, 0.923879504f, 0.903989315f,
0.881921291f, 0.857728601f, 0.831469595f, 0.803207517f, 0.773010433f,
0.740951121f, 0.707106769f, 0.671558976f, 0.634393275f, 0.595699310f,
0.555570245f, 0.514102757f, 0.471396744f, 0.427555084f, 0.382683426f,
0.336889863f, 0.290284663f, 0.242980182f, 0.195090324f, 0.146730468f,
0.0980171412f, 0.0490676761f, 0.707106769f, 0.989176512f, 0.956940353f,
0.903989315f, 0.831469595f, 0.740951121f, 0.634393275f, 0.514102757f,
0.382683426f, 0.242980182f, 0.0980171412f, -0.0490676761f, -0.195090324f,
-0.336889863f, -0.471396744f, -0.595699310f, -0.707106769f, -0.803207517f,
-0.881921291f, -0.941544056f, -0.980785251f, -0.998795450f, -0.995184720f,
-0.970031261f, -0.923879504f, -0.857728601f, -0.773010433f, -0.671558976f,
-0.555570245f, -0.427555084f, -0.290284663f, -0.146730468f, 0.707106769f,
0.970031261f, 0.881921291f, 0.740951121f, 0.555570245f, 0.336889863f,
0.0980171412f, -0.146730468f, -0.382683426f, -0.595699310f, -0.773010433f,
-0.903989315f, -0.980785251f, -0.998795450f, -0.956940353f, -0.857728601f,
-0.707106769f, -0.514102757f, -0.290284663f, -0.0490676761f, 0.195090324f,
0.427555084f, 0.634393275f, 0.803207517f, 0.923879504f, 0.989176512f,
0.995184720f, 0.941544056f, 0.831469595f, 0.671558976f, 0.471396744f,
0.242980182f, 0.707106769f, 0.941544056f, 0.773010433f, 0.514102757f,
0.195090324f, -0.146730468f, -0.471396744f, -0.740951121f, -0.923879504f,
-0.998795450f, -0.956940353f, -0.803207517f, -0.555570245f, -0.242980182f,
0.0980171412f, 0.427555084f, 0.707106769f, 0.903989315f, 0.995184720f,
0.970031261f, 0.831469595f, 0.595699310f, 0.290284663f, -0.0490676761f,
-0.382683426f, -0.671558976f, -0.881921291f, -0.989176512f, -0.980785251f,
-0.857728601f, -0.634393275f, -0.336889863f, 0.707106769f, 0.903989315f,
0.634393275f, 0.242980182f, -0.195090324f, -0.595699310f, -0.881921291f,
-0.998795450f, -0.923879504f, -0.671558976f, -0.290284663f, 0.146730468f,
0.555570245f, 0.857728601f, 0.995184720f, 0.941544056f, 0.707106769f,
0.336889863f, -0.0980171412f, -0.514102757f, -0.831469595f, -0.989176512f,
-0.956940353f, -0.740951121f, -0.382683426f, 0.0490676761f, 0.471396744f,
0.803207517f, 0.980785251f, 0.970031261f, 0.773010433f, 0.427555084f,
0.707106769f, 0.857728601f, 0.471396744f, -0.0490676761f, -0.555570245f,
-0.903989315f, -0.995184720f, -0.803207517f, -0.382683426f, 0.146730468f,
0.634393275f, 0.941544056f, 0.980785251f, 0.740951121f, 0.290284663f,
-0.242980182f, -0.707106769f, -0.970031261f, -0.956940353f, -0.671558976f,
-0.195090324f, 0.336889863f, 0.773010433f, 0.989176512f, 0.923879504f,
0.595699310f, 0.0980171412f, -0.427555084f, -0.831469595f, -0.998795450f,
-0.881921291f, -0.514102757f, 0.707106769f, 0.803207517f, 0.290284663f,
-0.336889863f, -0.831469595f, -0.998795450f, -0.773010433f, -0.242980182f,
0.382683426f, 0.857728601f, 0.995184720f, 0.740951121f, 0.195090324f,
-0.427555084f, -0.881921291f, -0.989176512f, -0.707106769f, -0.146730468f,
0.471396744f, 0.903989315f, 0.980785251f, 0.671558976f, 0.0980171412f,
-0.514102757f, -0.923879504f, -0.970031261f, -0.634393275f, -0.0490676761f,
0.555570245f, 0.941544056f, 0.956940353f, 0.595699310f, 0.707106769f,
0.740951121f, 0.0980171412f, -0.595699310f, -0.980785251f, -0.857728601f,
-0.290284663f, 0.427555084f, 0.923879504f, 0.941544056f, 0.471396744f,
-0.242980182f, -0.831469595f, -0.989176512f, -0.634393275f, 0.0490676761f,
0.707106769f, 0.998795450f, 0.773010433f, 0.146730468f, -0.555570245f,
-0.970031261f, -0.881921291f, -0.336889863f, 0.382683426f, 0.903989315f,
0.956940353f, 0.514102757f, -0.195090324f, -0.803207517f, -0.995184720f,
-0.671558976f, 0.707106769f, 0.671558976f, -0.0980171412f, -0.803207517f,
-0.980785251f, -0.514102757f, 0.290284663f, 0.903989315f, 0.923879504f,
0.336889863f, -0.471396744f, -0.970031261f, -0.831469595f, -0.146730468f,
0.634393275f, 0.998795450f, 0.707106769f, -0.0490676761f, -0.773010433f,
-0.989176512f, -0.555570245f, 0.242980182f, 0.881921291f, 0.941544056f,
0.382683426f, -0.427555084f, -0.956940353f, -0.857728601f, -0.195090324f,
0.595699310f, 0.995184720f, 0.740951121f, 0.707106769f, 0.595699310f,
-0.290284663f, -0.941544056f, -0.831469595f, -0.0490676761f, 0.773010433f,
0.970031261f, 0.382683426f, -0.514102757f, -0.995184720f, -0.671558976f,
0.195090324f, 0.903989315f, 0.881921291f, 0.146730468f, -0.707106769f,
-0.989176512f, -0.471396744f, 0.427555084f, 0.980785251f, 0.740951121f,
-0.0980171412f, -0.857728601f, -0.923879504f, -0.242980182f, 0.634393275f,
0.998795450f, 0.555570245f, -0.336889863f, -0.956940353f, -0.803207517f,
0.707106769f, 0.514102757f, -0.471396744f, -0.998795450f, -0.555570245f,
0.427555084f, 0.995184720f, 0.595699310f, -0.382683426f, -0.989176512f,
-0.634393275f, 0.336889863f, 0.980785251f, 0.671558976f, -0.290284663f,
-0.970031261f, -0.707106769f, 0.242980182f, 0.956940353f, 0.740951121f,
-0.195090324f, -0.941544056f, -0.773010433f, 0.146730468f, 0.923879504f,
0.803207517f, -0.0980171412f, -0.903989315f, -0.831469595f, 0.0490676761f,
0.881921291f, 0.857728601f, 0.707106769f, 0.427555084f, -0.634393275f,
-0.970031261f, -0.195090324f, 0.803207517f, 0.881921291f, -0.0490676761f,
-0.923879504f, -0.740951121f, 0.290284663f, 0.989176512f, 0.555570245f,
-0.514102757f, -0.995184720f, -0.336889863f, 0.707106769f, 0.941544056f,
0.0980171412f, -0.857728601f, -0.831469595f, 0.146730468f, 0.956940353f,
0.671558976f, -0.382683426f, -0.998795450f, -0.471396744f, 0.595699310f,
0.980785251f, 0.242980182f, -0.773010433f, -0.903989315f, 0.707106769f,
0.336889863f, -0.773010433f, -0.857728601f, 0.195090324f, 0.989176512f,
0.471396744f, -0.671558976f, -0.923879504f, 0.0490676761f, 0.956940353f,
0.595699310f, -0.555570245f, -0.970031261f, -0.0980171412f, 0.903989315f,
0.707106769f, -0.427555084f, -0.995184720f, -0.242980182f, 0.831469595f,
0.803207517f, -0.290284663f, -0.998795450f, -0.382683426f, 0.740951121f,
0.881921291f, -0.146730468f, -0.980785251f, -0.514102757f, 0.634393275f,
0.941544056f, 0.707106769f, 0.242980182f, -0.881921291f, -0.671558976f,
0.555570245f, 0.941544056f, -0.0980171412f, -0.989176512f, -0.382683426f,
0.803207517f, 0.773010433f, -0.427555084f, -0.980785251f, -0.0490676761f,
0.956940353f, 0.514102757f, -0.707106769f, -0.857728601f, 0.290284663f,
0.998795450f, 0.195090324f, -0.903989315f, -0.634393275f, 0.595699310f,
0.923879504f, -0.146730468f, -0.995184720f, -0.336889863f, 0.831469595f,
0.740951121f, -0.471396744f, -0.970031261f, 0.707106769f, 0.146730468f,
-0.956940353f, -0.427555084f, 0.831469595f, 0.671558976f, -0.634393275f,
-0.857728601f, 0.382683426f, 0.970031261f, -0.0980171412f, -0.998795450f,
-0.195090324f, 0.941544056f, 0.471396744f, -0.803207517f, -0.707106769f,
0.595699310f, 0.881921291f, -0.336889863f, -0.980785251f, 0.0490676761f,
0.995184720f, 0.242980182f, -0.923879504f, -0.514102757f, 0.773010433f,
0.740951121f, -0.555570245f, -0.903989315f, 0.290284663f, 0.989176512f,
0.707106769f, 0.0490676761f, -0.995184720f, -0.146730468f, 0.980785251f,
0.242980182f, -0.956940353f, -0.336889863f, 0.923879504f, 0.427555084f,
-0.881921291f, -0.514102757f, 0.831469595f, 0.595699310f, -0.773010433f,
-0.671558976f, 0.707106769f, 0.740951121f, -0.634393275f, -0.803207517f,
0.555570245f, 0.857728601f, -0.471396744f, -0.903989315f, 0.382683426f,
0.941544056f, -0.290284663f, -0.970031261f, 0.195090324f, 0.989176512f,
-0.0980171412f, -0.998795450f, 0.707106769f, -0.0490676761f, -0.995184720f,
0.146730468f, 0.980785251f, -0.242980182f, -0.956940353f, 0.336889863f,
0.923879504f, -0.427555084f, -0.881921291f, 0.514102757f, 0.831469595f,
-0.595699310f, -0.773010433f, 0.671558976f, 0.707106769f, -0.740951121f,
-0.634393275f, 0.803207517f, 0.555570245f, -0.857728601f, -0.471396744f,
0.903989315f, 0.382683426f, -0.941544056f, -0.290284663f, 0.970031261f,
0.195090324f, -0.989176512f, -0.0980171412f, 0.998795450f, 0.707106769f,
-0.146730468f, -0.956940353f, 0.427555084f, 0.831469595f, -0.671558976f,
-0.634393275f, 0.857728601f, 0.382683426f, -0.970031261f, -0.0980171412f,
0.998795450f, -0.195090324f, -0.941544056f, 0.471396744f, 0.803207517f,
-0.707106769f, -0.595699310f, 0.881921291f, 0.336889863f, -0.980785251f,
-0.0490676761f, 0.995184720f, -0.242980182f, -0.923879504f, 0.514102757f,
0.773010433f, -0.740951121f, -0.555570245f, 0.903989315f, 0.290284663f,
-0.989176512f, 0.707106769f, -0.242980182f, -0.881921291f, 0.671558976f,
0.555570245f, -0.941544056f, -0.0980171412f, 0.989176512f, -0.382683426f,
-0.803207517f, 0.773010433f, 0.427555084f, -0.980785251f, 0.0490676761f,
0.956940353f, -0.514102757f, -0.707106769f, 0.857728601f, 0.290284663f,
-0.998795450f, 0.195090324f, 0.903989315f, -0.634393275f, -0.595699310f,
0.923879504f, 0.146730468f, -0.995184720f, 0.336889863f, 0.831469595f,
-0.740951121f, -0.471396744f, 0.970031261f, 0.707106769f, -0.336889863f,
-0.773010433f, 0.857728601f, 0.195090324f, -0.989176512f, 0.471396744f,
0.671558976f, -0.923879504f, -0.0490676761f, 0.956940353f, -0.595699310f,
-0.555570245f, 0.970031261f, -0.0980171412f, -0.903989315f, 0.707106769f,
0.427555084f, -0.995184720f, 0.242980182f, 0.831469595f, -0.803207517f,
-0.290284663f, 0.998795450f, -0.382683426f, -0.740951121f, 0.881921291f,
0.146730468f, -0.980785251f, 0.514102757f, 0.634393275f, -0.941544056f,
0.707106769f, -0.427555084f, -0.634393275f, 0.970031261f, -0.195090324f,
-0.803207517f, 0.881921291f, 0.0490676761f, -0.923879504f, 0.740951121f,
0.290284663f, -0.989176512f, 0.555570245f, 0.514102757f, -0.995184720f,
0.336889863f, 0.707106769f, -0.941544056f, 0.0980171412f, 0.857728601f,
-0.831469595f, -0.146730468f, 0.956940353f, -0.671558976f, -0.382683426f,
0.998795450f, -0.471396744f, -0.595699310f, 0.980785251f, -0.242980182f,
-0.773010433f, 0.903989315f, 0.707106769f, -0.514102757f, -0.471396744f,
0.998795450f, -0.555570245f, -0.427555084f, 0.995184720f, -0.595699310f,
-0.382683426f, 0.989176512f, -0.634393275f, -0.336889863f, 0.980785251f,
-0.671558976f, -0.290284663f, 0.970031261f, -0.707106769f, -0.242980182f,
0.956940353f, -0.740951121f, -0.195090324f, 0.941544056f, -0.773010433f,
-0.146730468f, 0.923879504f, -0.803207517f, -0.0980171412f, 0.903989315f,
-0.831469595f, -0.0490676761f, 0.881921291f, -0.857728601f, 0.707106769f,
-0.595699310f, -0.290284663f, 0.941544056f, -0.831469595f, 0.0490676761f,
0.773010433f, -0.970031261f, 0.382683426f, 0.514102757f, -0.995184720f,
0.671558976f, 0.195090324f, -0.903989315f, 0.881921291f, -0.146730468f,
-0.707106769f, 0.989176512f, -0.471396744f, -0.427555084f, 0.980785251f,
-0.740951121f, -0.0980171412f, 0.857728601f, -0.923879504f, 0.242980182f,
0.634393275f, -0.998795450f, 0.555570245f, 0.336889863f, -0.956940353f,
0.803207517f, 0.707106769f, -0.671558976f, -0.0980171412f, 0.803207517f,
-0.980785251f, 0.514102757f, 0.290284663f, -0.903989315f, 0.923879504f,
-0.336889863f, -0.471396744f, 0.970031261f, -0.831469595f, 0.146730468f,
0.634393275f, -0.998795450f, 0.707106769f, 0.0490676761f, -0.773010433f,
0.989176512f, -0.555570245f, -0.242980182f, 0.881921291f, -0.941544056f,
0.382683426f, 0.427555084f, -0.956940353f, 0.857728601f, -0.195090324f,
-0.595699310f, 0.995184720f, -0.740951121f, 0.707106769f, -0.740951121f,
0.0980171412f, 0.595699310f, -0.980785251f, 0.857728601f, -0.290284663f,
-0.427555084f, 0.923879504f, -0.941544056f, 0.471396744f, 0.242980182f,
-0.831469595f, 0.989176512f, -0.634393275f, -0.0490676761f, 0.707106769f,
-0.998795450f, 0.773010433f, -0.146730468f, -0.555570245f, 0.970031261f,
-0.881921291f, 0.336889863f, 0.382683426f, -0.903989315f, 0.956940353f,
-0.514102757f, -0.195090324f, 0.803207517f, -0.995184720f, 0.671558976f,
0.707106769f, -0.803207517f, 0.290284663f, 0.336889863f, -0.831469595f,
0.998795450f, -0.773010433f, 0.242980182f, 0.382683426f, -0.857728601f,
0.995184720f, -0.740951121f, 0.195090324f, 0.427555084f, -0.881921291f,
0.989176512f, -0.707106769f, 0.146730468f, 0.471396744f, -0.903989315f,
0.980785251f, -0.671558976f, 0.0980171412f, 0.514102757f, -0.923879504f,
0.970031261f, -0.634393275f, 0.0490676761f, 0.555570245f, -0.941544056f,
0.956940353f, -0.595699310f, 0.707106769f, -0.857728601f, 0.471396744f,
0.0490676761f, -0.555570245f, 0.903989315f, -0.995184720f, 0.803207517f,
-0.382683426f, -0.146730468f, 0.634393275f, -0.941544056f, 0.980785251f,
-0.740951121f, 0.290284663f, 0.242980182f, -0.707106769f, 0.970031261f,
-0.956940353f, 0.671558976f, -0.195090324f, -0.336889863f, 0.773010433f,
-0.989176512f, 0.923879504f, -0.595699310f, 0.0980171412f, 0.427555084f,
-0.831469595f, 0.998795450f, -0.881921291f, 0.514102757f, 0.707106769f,
-0.903989315f, 0.634393275f, -0.242980182f, -0.195090324f, 0.595699310f,
-0.881921291f, 0.998795450f, -0.923879504f, 0.671558976f, -0.290284663f,
-0.146730468f, 0.555570245f, -0.857728601f, 0.995184720f, -0.941544056f,
0.707106769f, -0.336889863f, -0.0980171412f, 0.514102757f, -0.831469595f,
0.989176512f, -0.956940353f, 0.740951121f, -0.382683426f, -0.0490676761f,
0.471396744f, -0.803207517f, 0.980785251f, -0.970031261f, 0.773010433f,
-0.427555084f, 0.707106769f, -0.941544056f, 0.773010433f, -0.514102757f,
0.195090324f, 0.146730468f, -0.471396744f, 0.740951121f, -0.923879504f,
0.998795450f, -0.956940353f, 0.803207517f, -0.555570245f, 0.242980182f,
0.0980171412f, -0.427555084f, 0.707106769f, -0.903989315f, 0.995184720f,
-0.970031261f, 0.831469595f, -0.595699310f, 0.290284663f, 0.0490676761f,
-0.382683426f, 0.671558976f, -0.881921291f, 0.989176512f, -0.980785251f,
0.857728601f, -0.634393275f, 0.336889863f, 0.707106769f, -0.970031261f,
0.881921291f, -0.740951121f, 0.555570245f, -0.336889863f, 0.0980171412f,
0.146730468f, -0.382683426f, 0.595699310f, -0.773010433f, 0.903989315f,
-0.980785251f, 0.998795450f, -0.956940353f, 0.857728601f, -0.707106769f,
0.514102757f, -0.290284663f, 0.0490676761f, 0.195090324f, -0.427555084f,
0.634393275f, -0.803207517f, 0.923879504f, -0.989176512f, 0.995184720f,
-0.941544056f, 0.831469595f, -0.671558976f, 0.471396744f, -0.242980182f,
0.707106769f, -0.989176512f, 0.956940353f, -0.903989315f, 0.831469595f,
-0.740951121f, 0.634393275f, -0.514102757f, 0.382683426f, -0.242980182f,
0.0980171412f, 0.0490676761f, -0.195090324f, 0.336889863f, -0.471396744f,
0.595699310f, -0.707106769f, 0.803207517f, -0.881921291f, 0.941544056f,
-0.980785251f, 0.998795450f, -0.995184720f, 0.970031261f, -0.923879504f,
0.857728601f, -0.773010433f, 0.671558976f, -0.555570245f, 0.427555084f,
-0.290284663f, 0.146730468f, 0.707106769f, -0.998795450f, 0.995184720f,
-0.989176512f, 0.980785251f, -0.970031261f, 0.956940353f, -0.941544056f,
0.923879504f, -0.903989315f, 0.881921291f, -0.857728601f, 0.831469595f,
-0.803207517f, 0.773010433f, -0.740951121f, 0.707106769f, -0.671558976f,
0.634393275f, -0.595699310f, 0.555570245f, -0.514102757f, 0.471396744f,
-0.427555084f, 0.382683426f, -0.336889863f, 0.290284663f, -0.242980182f,
0.195090324f, -0.146730468f, 0.0980171412f, -0.0490676761f, };

View File

@@ -1,388 +0,0 @@
/* Copyright (c) 2018 Mozilla
2008-2011 Octasic Inc.
2012-2017 Jean-Marc Valin */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef VEC_H
#define VEC_H
#include "opus_types.h"
#include "common.h"
#include <math.h>
#include "arch.h"
#include "x86/x86_arch_macros.h"
#if defined(__AVX__) || defined(__SSE2__)
#include "vec_avx.h"
#elif (defined(__ARM_NEON__) || defined(__ARM_NEON)) && !defined(DISABLE_NEON)
#include "vec_neon.h"
#else
#define MAX_INPUTS (2048)
#define NO_OPTIMIZATIONS
static inline void sgemv16x1(float *out, const float *weights, int rows, int cols, int col_stride, const float *x)
{
int i, j;
RNN_CLEAR(out, rows);
for (i=0;i<rows;i+=16)
{
for (j=0;j<cols;j++)
{
const float * restrict w;
float * restrict y;
float xj;
w = &weights[j*col_stride + i];
xj = x[j];
y = &out[i];
y[0] += w[0]*xj;
y[1] += w[1]*xj;
y[2] += w[2]*xj;
y[3] += w[3]*xj;
y[4] += w[4]*xj;
y[5] += w[5]*xj;
y[6] += w[6]*xj;
y[7] += w[7]*xj;
y[8] += w[8]*xj;
y[9] += w[9]*xj;
y[10] += w[10]*xj;
y[11] += w[11]*xj;
y[12] += w[12]*xj;
y[13] += w[13]*xj;
y[14] += w[14]*xj;
y[15] += w[15]*xj;
}
}
}
static inline void sgemv8x1(float *out, const float *weights, int rows, int cols, int col_stride, const float *x)
{
int i, j;
RNN_CLEAR(out, rows);
for (i=0;i<rows;i+=8)
{
for (j=0;j<cols;j++)
{
const float * restrict w;
float * restrict y;
float xj;
w = &weights[j*col_stride + i];
xj = x[j];
y = &out[i];
y[0] += w[0]*xj;
y[1] += w[1]*xj;
y[2] += w[2]*xj;
y[3] += w[3]*xj;
y[4] += w[4]*xj;
y[5] += w[5]*xj;
y[6] += w[6]*xj;
y[7] += w[7]*xj;
}
}
}
static inline void sgemv(float *out, const float *weights, int rows, int cols, int col_stride, const float *x)
{
if ((rows&0xf) == 0) sgemv16x1(out, weights, rows, cols, col_stride, x);
else if ((rows&0x7) == 0) sgemv8x1(out, weights, rows, cols, col_stride, x);
else {
int i, j;
for (i=0;i<rows;i++)
{
out[i] = 0;
for (j=0;j<cols;j++) out[i] += weights[j*col_stride + i]*x[j];
}
}
}
static inline void sparse_sgemv8x4(float *out, const float *w, const int *idx, int rows, const float *x)
{
int i, j;
RNN_CLEAR(out, rows);
for (i=0;i<rows;i+=8)
{
int cols;
cols = *idx++;
for (j=0;j<cols;j++)
{
int pos;
float * restrict y;
float xj0, xj1, xj2, xj3;
pos = (*idx++);
xj0 = x[pos+0];
xj1 = x[pos+1];
xj2 = x[pos+2];
xj3 = x[pos+3];
y = &out[i];
y[0] += w[0]*xj0;
y[1] += w[1]*xj0;
y[2] += w[2]*xj0;
y[3] += w[3]*xj0;
y[4] += w[4]*xj0;
y[5] += w[5]*xj0;
y[6] += w[6]*xj0;
y[7] += w[7]*xj0;
y[0] += w[8]*xj1;
y[1] += w[9]*xj1;
y[2] += w[10]*xj1;
y[3] += w[11]*xj1;
y[4] += w[12]*xj1;
y[5] += w[13]*xj1;
y[6] += w[14]*xj1;
y[7] += w[15]*xj1;
y[0] += w[16]*xj2;
y[1] += w[17]*xj2;
y[2] += w[18]*xj2;
y[3] += w[19]*xj2;
y[4] += w[20]*xj2;
y[5] += w[21]*xj2;
y[6] += w[22]*xj2;
y[7] += w[23]*xj2;
y[0] += w[24]*xj3;
y[1] += w[25]*xj3;
y[2] += w[26]*xj3;
y[3] += w[27]*xj3;
y[4] += w[28]*xj3;
y[5] += w[29]*xj3;
y[6] += w[30]*xj3;
y[7] += w[31]*xj3;
w += 32;
}
}
}
#ifdef USE_SU_BIAS
static inline void sparse_cgemv8x4(float *out, const opus_int8 *w, const int *idx, const float *scale, int rows, int cols, const float *_x)
{
int i, j;
unsigned char x[MAX_INPUTS];
for (i=0;i<rows;i++) out[i] = 0;
for (i=0;i<cols;i++) x[i] = 127+floor(.5+127*_x[i]);
for (i=0;i<rows;i+=8)
{
int colblocks;
colblocks = *idx++;
for (j=0;j<colblocks;j++)
{
int pos;
float * restrict y;
int xj0, xj1, xj2, xj3;
pos = (*idx++);
xj0 = x[pos+0];
xj1 = x[pos+1];
xj2 = x[pos+2];
xj3 = x[pos+3];
y = &out[i];
y[0] += (w[0]*xj0+w[1]*xj1+w[2]*xj2+w[3]*xj3);
y[1] += (w[4]*xj0+w[5]*xj1+w[6]*xj2+w[7]*xj3);
y[2] += (w[8]*xj0+w[9]*xj1+w[10]*xj2+w[11]*xj3);
y[3] += (w[12]*xj0+w[13]*xj1+w[14]*xj2+w[15]*xj3);
y[4] += (w[16]*xj0+w[17]*xj1+w[18]*xj2+w[19]*xj3);
y[5] += (w[20]*xj0+w[21]*xj1+w[22]*xj2+w[23]*xj3);
y[6] += (w[24]*xj0+w[25]*xj1+w[26]*xj2+w[27]*xj3);
y[7] += (w[28]*xj0+w[29]*xj1+w[30]*xj2+w[31]*xj3);
w += 32;
}
}
for (i=0;i<rows;i++) out[i] *= scale[i];
}
static inline void cgemv8x4(float *out, const opus_int8 *w, const float *scale, int rows, int cols, const float *_x)
{
int i, j;
unsigned char x[MAX_INPUTS];
for (i=0;i<rows;i++) out[i] = 0;
for (i=0;i<cols;i++) x[i] = 127+(int)floor(.5+127*_x[i]);
for (i=0;i<rows;i+=8)
{
for (j=0;j<cols;j+=4)
{
float *y;
float xj0, xj1, xj2, xj3;
xj0 = x[j+0];
xj1 = x[j+1];
xj2 = x[j+2];
xj3 = x[j+3];
y = &out[i];
y[0] += (w[0]*xj0+w[1]*xj1+w[2]*xj2+w[3]*xj3);
y[1] += (w[4]*xj0+w[5]*xj1+w[6]*xj2+w[7]*xj3);
y[2] += (w[8]*xj0+w[9]*xj1+w[10]*xj2+w[11]*xj3);
y[3] += (w[12]*xj0+w[13]*xj1+w[14]*xj2+w[15]*xj3);
y[4] += (w[16]*xj0+w[17]*xj1+w[18]*xj2+w[19]*xj3);
y[5] += (w[20]*xj0+w[21]*xj1+w[22]*xj2+w[23]*xj3);
y[6] += (w[24]*xj0+w[25]*xj1+w[26]*xj2+w[27]*xj3);
y[7] += (w[28]*xj0+w[29]*xj1+w[30]*xj2+w[31]*xj3);
w += 32;
}
}
for (i=0;i<rows;i++) out[i] *= scale[i];
}
#else
static inline void sparse_cgemv8x4(float *out, const opus_int8 *w, const int *idx, const float *scale, int rows, int cols, const float *_x)
{
int i, j;
opus_int8 x[MAX_INPUTS];
for (i=0;i<rows;i++) out[i] = 0;
for (i=0;i<cols;i++) x[i] = (int)floor(.5+127*_x[i]);
for (i=0;i<rows;i+=8)
{
int colblocks;
colblocks = *idx++;
for (j=0;j<colblocks;j++)
{
int pos;
float * restrict y;
int xj0, xj1, xj2, xj3;
pos = (*idx++);
xj0 = x[pos+0];
xj1 = x[pos+1];
xj2 = x[pos+2];
xj3 = x[pos+3];
y = &out[i];
y[0] += (w[0]*xj0+w[1]*xj1+w[2]*xj2+w[3]*xj3);
y[1] += (w[4]*xj0+w[5]*xj1+w[6]*xj2+w[7]*xj3);
y[2] += (w[8]*xj0+w[9]*xj1+w[10]*xj2+w[11]*xj3);
y[3] += (w[12]*xj0+w[13]*xj1+w[14]*xj2+w[15]*xj3);
y[4] += (w[16]*xj0+w[17]*xj1+w[18]*xj2+w[19]*xj3);
y[5] += (w[20]*xj0+w[21]*xj1+w[22]*xj2+w[23]*xj3);
y[6] += (w[24]*xj0+w[25]*xj1+w[26]*xj2+w[27]*xj3);
y[7] += (w[28]*xj0+w[29]*xj1+w[30]*xj2+w[31]*xj3);
w += 32;
}
}
for (i=0;i<rows;i++) out[i] *= scale[i];
}
static inline void cgemv8x4(float *out, const opus_int8 *w, const float *scale, int rows, int cols, const float *_x)
{
int i, j;
opus_int8 x[MAX_INPUTS];
for (i=0;i<rows;i++) out[i] = 0;
for (i=0;i<cols;i++) x[i] = (int)floor(.5+127*_x[i]);
for (i=0;i<rows;i+=8)
{
for (j=0;j<cols;j+=4)
{
float *y;
float xj0, xj1, xj2, xj3;
xj0 = x[j+0];
xj1 = x[j+1];
xj2 = x[j+2];
xj3 = x[j+3];
y = &out[i];
y[0] += (w[0]*xj0+w[1]*xj1+w[2]*xj2+w[3]*xj3);
y[1] += (w[4]*xj0+w[5]*xj1+w[6]*xj2+w[7]*xj3);
y[2] += (w[8]*xj0+w[9]*xj1+w[10]*xj2+w[11]*xj3);
y[3] += (w[12]*xj0+w[13]*xj1+w[14]*xj2+w[15]*xj3);
y[4] += (w[16]*xj0+w[17]*xj1+w[18]*xj2+w[19]*xj3);
y[5] += (w[20]*xj0+w[21]*xj1+w[22]*xj2+w[23]*xj3);
y[6] += (w[24]*xj0+w[25]*xj1+w[26]*xj2+w[27]*xj3);
y[7] += (w[28]*xj0+w[29]*xj1+w[30]*xj2+w[31]*xj3);
w += 32;
}
}
for (i=0;i<rows;i++) out[i] *= scale[i];
}
#endif
/* No AVX2/FMA support */
#ifndef LPCNET_TEST
static inline float lpcnet_exp2(float x)
{
int integer;
float frac;
union {
float f;
opus_uint32 i;
} res;
integer = floor(x);
if (integer < -50)
return 0;
frac = x-integer;
/* K0 = 1, K1 = log(2), K2 = 3-4*log(2), K3 = 3*log(2) - 2 */
res.f = 0.99992522f + frac * (0.69583354f
+ frac * (0.22606716f + 0.078024523f*frac));
res.i = (res.i + (integer<<23)) & 0x7fffffff;
return res.f;
}
#define lpcnet_exp(x) lpcnet_exp2((x)*1.44269504f)
#define fmadd(a, b, c) ((a)*(b)+(c))
static OPUS_INLINE float tanh_approx(float x)
{
const float N0 = 952.52801514f;
const float N1 = 96.39235687f;
const float N2 = 0.60863042f;
const float D0 = 952.72399902f;
const float D1 = 413.36801147f;
const float D2 = 11.88600922f;
float X2, num, den;
X2 = x*x;
num = fmadd(fmadd(N2, X2, N1), X2, N0);
den = fmadd(fmadd(D2, X2, D1), X2, D0);
num = num*x/den;
return MAX32(-1.f, MIN32(1.f, num));
}
static inline float sigmoid_approx(float x)
{
return .5f + .5f*tanh_approx(.5f*x);
}
static inline void softmax(float *y, const float *x, int N)
{
int i;
for (i=0;i<N;i++)
y[i] = lpcnet_exp(x[i]);
}
static inline void vec_tanh(float *y, const float *x, int N)
{
int i;
for (i=0;i<N;i++)
{
y[i] = tanh_approx(x[i]);
}
}
static inline void vec_sigmoid(float *y, const float *x, int N)
{
int i;
for (i=0;i<N;i++)
{
y[i] = sigmoid_approx(x[i]);
}
}
#endif
#define SCALE (128.f*127.f)
#define SCALE_1 (1.f/128.f/127.f)
#endif /*no optimizations*/
#endif /*VEC_H*/

View File

@@ -1,85 +0,0 @@
/* Copyright (c) 2011-2019 Mozilla
2023 Amazon */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef DNN_X86_H
#define DNN_X86_H
#include "cpu_support.h"
#include "opus_types.h"
void compute_linear_sse4_1(const LinearLayer *linear, float *out, const float *in);
void compute_activation_sse4_1(float *output, const float *input, int N, int activation);
void compute_conv2d_sse4_1(const Conv2dLayer *conv, float *out, float *mem, const float *in, int height, int hstride, int activation);
void compute_linear_avx2(const LinearLayer *linear, float *out, const float *in);
void compute_activation_avx2(float *output, const float *input, int N, int activation);
void compute_conv2d_avx2(const Conv2dLayer *conv, float *out, float *mem, const float *in, int height, int hstride, int activation);
#ifdef RNN_ENABLE_X86_RTCD
extern void (*const RNN_COMPUTE_LINEAR_IMPL[OPUS_ARCHMASK + 1])(
const LinearLayer *linear,
float *out,
const float *in
);
#define OVERRIDE_COMPUTE_LINEAR
#define compute_linear(linear, out, in, arch) \
((*RNN_COMPUTE_LINEAR_IMPL[(arch) & OPUS_ARCHMASK])(linear, out, in))
extern void (*const RNN_COMPUTE_ACTIVATION_IMPL[OPUS_ARCHMASK + 1])(
float *output,
const float *input,
int N,
int activation
);
#define OVERRIDE_COMPUTE_ACTIVATION
#define compute_activation(output, input, N, activation, arch) \
((*RNN_COMPUTE_ACTIVATION_IMPL[(arch) & OPUS_ARCHMASK])(output, input, N, activation))
extern void (*const RNN_COMPUTE_CONV2D_IMPL[OPUS_ARCHMASK + 1])(
const Conv2dLayer *conv,
float *out,
float *mem,
const float *in,
int height,
int hstride,
int activation
);
#define OVERRIDE_COMPUTE_CONV2D
#define compute_conv2d(conv, out, mem, in, height, hstride, activation, arch) \
((*RNN_COMPUTE_CONV2D_IMPL[(arch) & OPUS_ARCHMASK])(conv, out, mem, in, height, hstride, activation))
#endif
#endif /* DNN_X86_H */

View File

@@ -1,40 +0,0 @@
/* Copyright (c) 2018-2019 Mozilla
2023 Amazon */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "x86/x86_arch_macros.h"
#ifndef __AVX2__
#error nnet_avx2.c is being compiled without AVX2 enabled
#endif
#define RTCD_ARCH avx2
#include "nnet_arch.h"

View File

@@ -1,40 +0,0 @@
/* Copyright (c) 2018-2019 Mozilla
2023 Amazon */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "x86/x86_arch_macros.h"
#ifndef __SSE4_1__
#error nnet_sse4_1.c is being compiled without SSE4.1 enabled
#endif
#define RTCD_ARCH sse4_1
#include "nnet_arch.h"

View File

@@ -1,47 +0,0 @@
/* Copyright (c) 2023 Amazon */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef _MSC_VER
# ifdef OPUS_X86_MAY_HAVE_SSE
# ifndef __SSE__
# define __SSE__
# endif
# endif
# ifdef OPUS_X86_MAY_HAVE_SSE2
# ifndef __SSE2__
# define __SSE2__
# endif
# endif
# ifdef OPUS_X86_MAY_HAVE_SSE4_1
# ifndef __SSE4_1__
# define __SSE4_1__
# endif
# endif
#endif

View File

@@ -1,74 +0,0 @@
/* Copyright (c) 2018-2019 Mozilla
2023 Amazon */
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "x86/x86cpu.h"
#include "nnet.h"
#ifdef RNN_ENABLE_X86_RTCD
void (*const RNN_COMPUTE_LINEAR_IMPL[OPUS_ARCHMASK + 1])(
const LinearLayer *linear,
float *out,
const float *in
) = {
compute_linear_c, /* non-sse */
MAY_HAVE_SSE4_1(compute_linear), /* sse4.1 */
MAY_HAVE_AVX2(compute_linear) /* avx */
};
void (*const RNN_COMPUTE_ACTIVATION_IMPL[OPUS_ARCHMASK + 1])(
float *output,
const float *input,
int N,
int activation
) = {
compute_activation_c, /* non-sse */
MAY_HAVE_SSE4_1(compute_activation), /* sse4.1 */
MAY_HAVE_AVX2(compute_activation) /* avx */
};
void (*const RNN_COMPUTE_CONV2D_IMPL[OPUS_ARCHMASK + 1])(
const Conv2dLayer *conv,
float *out,
float *mem,
const float *in,
int height,
int hstride,
int activation
) = {
compute_conv2d_c, /* non-sse */
MAY_HAVE_SSE4_1(compute_conv2d), /* sse4.1 */
MAY_HAVE_AVX2(compute_conv2d) /* avx */
};
#endif

View File

@@ -1,166 +0,0 @@
/* Copyright (c) 2014, Cisco Systems, INC
Written by XiangMingZhu WeiZhou MinPeng YanWang
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "cpu_support.h"
#include "pitch.h"
#include "x86cpu.h"
#ifdef RNN_ENABLE_X86_RTCD
#if defined(_MSC_VER)
#include <intrin.h>
static _inline void cpuid(unsigned int CPUInfo[4], unsigned int InfoType)
{
__cpuid((int*)CPUInfo, InfoType);
}
#else
#if defined(CPU_INFO_BY_C)
#include <cpuid.h>
#endif
static void cpuid(unsigned int CPUInfo[4], unsigned int InfoType)
{
#if defined(CPU_INFO_BY_ASM)
#if defined(__i386__) && defined(__PIC__)
/* %ebx is PIC register in 32-bit, so mustn't clobber it. */
__asm__ __volatile__ (
"xchg %%ebx, %1\n"
"cpuid\n"
"xchg %%ebx, %1\n":
"=a" (CPUInfo[0]),
"=r" (CPUInfo[1]),
"=c" (CPUInfo[2]),
"=d" (CPUInfo[3]) :
/* We clear ECX to avoid a valgrind false-positive prior to v3.17.0. */
"0" (InfoType), "2" (0)
);
#else
__asm__ __volatile__ (
"cpuid":
"=a" (CPUInfo[0]),
"=b" (CPUInfo[1]),
"=c" (CPUInfo[2]),
"=d" (CPUInfo[3]) :
/* We clear ECX to avoid a valgrind false-positive prior to v3.17.0. */
"0" (InfoType), "2" (0)
);
#endif
#elif defined(CPU_INFO_BY_C)
/* We use __get_cpuid_count to clear ECX to avoid a valgrind false-positive
prior to v3.17.0.*/
if (!__get_cpuid_count(InfoType, 0, &(CPUInfo[0]), &(CPUInfo[1]), &(CPUInfo[2]), &(CPUInfo[3]))) {
/* Our function cannot fail, but __get_cpuid{_count} can.
Returning all zeroes will effectively disable all SIMD, which is
what we want on CPUs that don't support CPUID. */
CPUInfo[3] = CPUInfo[2] = CPUInfo[1] = CPUInfo[0] = 0;
}
#else
# error "Configured to use x86 RTCD, but no CPU detection method available. " \
"Reconfigure with --disable-rtcd (or send patches)."
#endif
}
#endif
typedef struct CPU_Feature{
/* SIMD: 128-bit */
int HW_SSE;
int HW_SSE2;
int HW_SSE41;
/* SIMD: 256-bit */
int HW_AVX2;
} CPU_Feature;
static void rnn_cpu_feature_check(CPU_Feature *cpu_feature)
{
unsigned int info[4];
unsigned int nIds = 0;
cpuid(info, 0);
nIds = info[0];
if (nIds >= 1){
cpuid(info, 1);
cpu_feature->HW_SSE = (info[3] & (1 << 25)) != 0;
cpu_feature->HW_SSE2 = (info[3] & (1 << 26)) != 0;
cpu_feature->HW_SSE41 = (info[2] & (1 << 19)) != 0;
cpu_feature->HW_AVX2 = (info[2] & (1 << 28)) != 0 && (info[2] & (1 << 12)) != 0;
if (cpu_feature->HW_AVX2 && nIds >= 7) {
cpuid(info, 7);
cpu_feature->HW_AVX2 = cpu_feature->HW_AVX2 && (info[1] & (1 << 5)) != 0;
} else {
cpu_feature->HW_AVX2 = 0;
}
}
else {
cpu_feature->HW_SSE = 0;
cpu_feature->HW_SSE2 = 0;
cpu_feature->HW_SSE41 = 0;
cpu_feature->HW_AVX2 = 0;
}
}
static int rnn_select_arch_impl(void)
{
CPU_Feature cpu_feature;
int arch;
rnn_cpu_feature_check(&cpu_feature);
arch = 0;
if (!cpu_feature.HW_SSE41)
{
return arch;
}
arch++;
if (!cpu_feature.HW_AVX2)
{
return arch;
}
arch++;
return arch;
}
int rnn_select_arch(void) {
int arch = rnn_select_arch_impl();
#ifdef FUZZING
/* Randomly downgrade the architecture. */
arch = rand()%(arch+1);
#endif
return arch;
}
#endif

View File

@@ -1,88 +0,0 @@
/* Copyright (c) 2014, Cisco Systems, INC
Written by XiangMingZhu WeiZhou MinPeng YanWang
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if !defined(X86CPU_H)
# define X86CPU_H
# define MAY_HAVE_SSE4_1(name) name ## _sse4_1
# define MAY_HAVE_AVX2(name) name ## _avx2
# ifdef RNN_ENABLE_X86_RTCD
int opus_select_arch(void);
# endif
# if defined(__SSE2__)
# include "common.h"
/*MOVD should not impose any alignment restrictions, but the C standard does,
and UBSan will report errors if we actually make unaligned accesses.
Use this to work around those restrictions (which should hopefully all get
optimized to a single MOVD instruction).
GCC implemented _mm_loadu_si32() since GCC 11; HOWEVER, there is a bug!
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99754
LLVM implemented _mm_loadu_si32() since Clang 8.0, however the
__clang_major__ version number macro is unreliable, as vendors
(specifically, Apple) will use different numbering schemes than upstream.
Clang's advice is "use feature detection", but they do not provide feature
detection support for specific SIMD functions.
We follow the approach from the SIMDe project and instead detect unrelated
features that should be available in the version we want (see
<https://github.com/simd-everywhere/simde/blob/master/simde/simde-detect-clang.h>).*/
# if defined(__clang__)
# if __has_warning("-Wextra-semi-stmt") || \
__has_builtin(__builtin_rotateleft32)
# define OPUS_CLANG_8 (1)
# endif
# endif
# if !defined(_MSC_VER) && !OPUS_GNUC_PREREQ(11,3) && !defined(OPUS_CLANG_8)
# include <string.h>
# include <emmintrin.h>
# ifdef _mm_loadu_si32
# undef _mm_loadu_si32
# endif
# define _mm_loadu_si32 WORKAROUND_mm_loadu_si32
static inline __m128i WORKAROUND_mm_loadu_si32(void const* mem_addr) {
int val;
memcpy(&val, mem_addr, sizeof(val));
return _mm_cvtsi32_si128(val);
}
# elif defined(_MSC_VER)
/* MSVC needs this for _mm_loadu_si32 */
# include <immintrin.h>
# endif
# define OP_CVTEPI8_EPI32_M32(x) \
(_mm_cvtepi8_epi32(_mm_loadu_si32(x)))
# define OP_CVTEPI16_EPI32_M64(x) \
(_mm_cvtepi16_epi32(_mm_loadl_epi64((__m128i *)(void*)(x))))
# endif
#endif

View File

@@ -0,0 +1,75 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Windows.Foundation;
namespace Microsoft.CmdPal.Common.Services;
public interface IExtensionService
{
/// <summary>
/// Gets the currently cached installed Command Palette extensions.
/// </summary>
/// <param name="includeDisabledExtensions">True to include disabled extensions in the result.</param>
/// <returns>A sequence of installed Command Palette extensions from the current in-memory cache.</returns>
Task<IEnumerable<IExtensionWrapper>> GetInstalledExtensionsAsync(bool includeDisabledExtensions = false);
/// <summary>
/// Forces a fresh scan of installed Command Palette extensions and updates the in-memory cache.
/// </summary>
/// <param name="includeDisabledExtensions">True to include disabled extensions in the result.</param>
/// <returns>A sequence of installed Command Palette extensions after the cache has been rebuilt.</returns>
Task<IEnumerable<IExtensionWrapper>> RefreshInstalledExtensionsAsync(bool includeDisabledExtensions = false);
// Task<IEnumerable<string>> GetInstalledHomeWidgetPackageFamilyNamesAsync(bool includeDisabledExtensions = false);
/// <summary>
/// Gets the installed Command Palette extensions for a specific provider type.
/// </summary>
/// <param name="providerType">The provider type to match.</param>
/// <param name="includeDisabledExtensions">True to include disabled extensions in the result.</param>
/// <returns>A sequence of installed Command Palette extensions for the requested provider type.</returns>
Task<IEnumerable<IExtensionWrapper>> GetInstalledExtensionsAsync(Microsoft.CommandPalette.Extensions.ProviderType providerType, bool includeDisabledExtensions = false);
/// <summary>
/// Gets a cached installed extension by its unique id.
/// </summary>
/// <param name="extensionUniqueId">The unique id of the extension to look up.</param>
/// <returns>The cached extension if found; otherwise, null.</returns>
IExtensionWrapper? GetInstalledExtension(string extensionUniqueId);
/// <summary>
/// Signals running extensions to stop.
/// </summary>
Task SignalStopExtensionsAsync();
/// <summary>
/// Raised when one or more extensions are added to the installed set.
/// </summary>
event TypedEventHandler<IExtensionService, IEnumerable<IExtensionWrapper>>? OnExtensionAdded;
/// <summary>
/// Raised when one or more extensions are removed from the installed set.
/// </summary>
event TypedEventHandler<IExtensionService, IEnumerable<IExtensionWrapper>>? OnExtensionRemoved;
/// <summary>
/// Enables an installed extension by unique id.
/// </summary>
/// <param name="extensionUniqueId">The unique id of the extension to enable.</param>
void EnableExtension(string extensionUniqueId);
/// <summary>
/// Disables an installed extension by unique id.
/// </summary>
/// <param name="extensionUniqueId">The unique id of the extension to disable.</param>
void DisableExtension(string extensionUniqueId);
///// <summary>
///// Gets a boolean indicating whether the extension was disabled due to the corresponding Windows optional feature
///// being absent from the machine or in an unknown state.
///// </summary>
///// <param name="extension">The out of proc extension object</param>
///// <returns>True only if the extension was disabled. False otherwise.</returns>
// public Task<bool> DisableExtensionIfWindowsFeatureNotAvailable(IExtensionWrapper extension);
}

View File

@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using System.Text.Json.Serialization;
namespace Microsoft.CmdPal.UI.ViewModels;
@@ -20,6 +19,9 @@ public record AppStateModel
init => _recentCommands = value;
}
// HERE BE DRAGONS: Using an ImmutableList<T> for a setting may explode in
// AOT builds. Make sure to test IN AOT setting this setting to null, [],
// and and array with values.
private ImmutableList<string>? _runHistory = ImmutableList<string>.Empty;
public ImmutableList<string> RunHistory

View File

@@ -125,49 +125,6 @@ public sealed class CommandProviderWrapper : ICommandProviderContext
isValid = true;
}
/// <summary>
/// Creates a wrapper for a JavaScript extension where the <see cref="ICommandProvider"/>
/// is obtained directly (not through <see cref="IExtensionWrapper.GetExtensionObject"/>).
/// </summary>
/// <param name="extension">The JS extension wrapper managing the node process.</param>
/// <param name="provider">The command provider proxy obtained via JSON-RPC.</param>
/// <param name="mainThread">The UI thread scheduler.</param>
public CommandProviderWrapper(IExtensionWrapper extension, ICommandProvider provider, TaskScheduler mainThread)
{
_taskScheduler = mainThread;
TopLevelPageContext = new(this, _taskScheduler);
Extension = extension;
ExtensionHost = new CommandPaletteHost(extension);
_commandProvider = new(provider);
try
{
var model = _commandProvider.Unsafe!;
model.InitializeWithHost(ExtensionHost);
model.ItemsChanged += CommandProvider_ItemsChanged;
isValid = true;
Id = provider.Id;
DisplayName = provider.DisplayName;
Icon = new(provider.Icon);
Icon.InitializeProperties();
Settings = new(provider.Settings, this, _taskScheduler);
Logger.LogDebug($"Initialized JS extension command provider {Extension.PackageFamilyName}:{Extension.ExtensionUniqueId}");
}
catch (Exception e)
{
Logger.LogError("Failed to initialize CommandProvider for JS extension.");
Logger.LogError($"Extension was {Extension!.PackageFamilyName}");
Logger.LogError(e.ToString());
}
isValid = true;
}
private ProviderSettings GetProviderSettings(SettingsModel settings)
{
if (!settings.ProviderSettings.TryGetValue(ProviderId, out var ps))

View File

@@ -2,7 +2,6 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.UI.ViewModels.Commands;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
@@ -19,7 +18,8 @@ public sealed partial class BuiltInsCommandProvider : CommandProvider
private readonly FallbackReloadItem _fallbackReloadItem = new();
private readonly FallbackLogItem _fallbackLogItem = new();
private readonly NewExtensionPage _newExtension = new();
private readonly GoHomeDockCommand _goHomeDockCommand = new();
private readonly IRootPageService _rootPageService;
public override ICommandItem[] TopLevelCommands() =>
[
@@ -41,16 +41,22 @@ public sealed partial class BuiltInsCommandProvider : CommandProvider
_fallbackLogItem,
];
public BuiltInsCommandProvider()
public BuiltInsCommandProvider(IRootPageService rootPageService)
{
Id = "com.microsoft.cmdpal.builtin.core";
DisplayName = Properties.Resources.builtin_display_name;
Icon = IconHelpers.FromRelativePath("Assets\\Square44x44Logo.altform-unplated_targetsize-256.png");
_rootPageService = rootPageService;
}
public override ICommandItem[]? GetDockBands()
{
return [new WrappedDockItem(_goHomeDockCommand, Properties.Resources.builtin_command_palette_title)];
var rootPage = _rootPageService.GetRootPage();
List<ICommandItem> bandItems = new();
bandItems.Add(new WrappedDockItem(rootPage, Properties.Resources.builtin_command_palette_title));
return bandItems.ToArray();
}
public override void InitializeWithHost(IExtensionHost host) => BuiltinsExtensionHost.Instance.Initialize(host);

View File

@@ -1,23 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.UI.ViewModels.Commands;
/// <summary>
/// A lightweight command used as a dock band item that navigates the user
/// back to the Command Palette home page when invoked.
/// </summary>
internal sealed partial class GoHomeDockCommand : InvokableCommand
{
public GoHomeDockCommand()
{
Name = Properties.Resources.builtin_command_palette_title;
Icon = IconHelpers.FromRelativePath("Assets\\Square44x44Logo.altform-unplated_targetsize-256.png");
}
public override ICommandResult Invoke() => CommandResult.GoHome();
}

View File

@@ -36,7 +36,6 @@ public sealed partial class ExtensionGalleryItemViewModel : ObservableObject
private const string SourceTypeWinGet = "winget";
private const string SourceTypeStore = "msstore";
private const string SourceTypeNpm = "npm";
private const string SourceTypeUrl = "url";
private const string SourceTypeGitHub = "github";
private const string SourceTypeWebsite = "website";
@@ -130,10 +129,6 @@ public sealed partial class ExtensionGalleryItemViewModel : ObservableObject
public bool HasStoreSource => HasSource(SourceTypeStore);
public bool HasNpmSource => HasSource(SourceTypeNpm);
public string? NpmPackageId => GetSource(SourceTypeNpm)?.Id;
public bool HasUrlSource => _installSourcesByType.ContainsKey(SourceTypeUrl) && InstallUrl is not null;
public bool HasHomepage => _homepageHttpUri is not null;
@@ -171,7 +166,7 @@ public sealed partial class ExtensionGalleryItemViewModel : ObservableObject
public bool ShowUnknownSourceIndicator => HasUnknownSource || !HasKnownSourceIndicator;
public bool HasActionableSourceDetails => HasStoreSource || HasWinGetSource || HasNpmSource || HasHomepage || HasUrlSource;
public bool HasActionableSourceDetails => HasStoreSource || HasWinGetSource || HasHomepage || HasUrlSource;
public bool ShowNoSourceDetails => !HasActionableSourceDetails;

View File

@@ -13,7 +13,6 @@ using Microsoft.CmdPal.Common.Services;
using Microsoft.CmdPal.Common.WinGet.Models;
using Microsoft.CmdPal.Common.WinGet.Services;
using Microsoft.CmdPal.UI.ViewModels.Properties;
using Microsoft.CmdPal.UI.ViewModels.Services;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Microsoft.Extensions.Logging;
@@ -51,7 +50,7 @@ public sealed partial class ExtensionGalleryViewModel : ObservableObject, IDispo
"Failed to check WinGet package status");
private readonly IExtensionGalleryService _galleryService;
private readonly IEnumerable<IExtensionService> _extensionServices;
private readonly IExtensionService _extensionService;
private readonly ILogger<ExtensionGalleryViewModel> _logger;
private readonly ExtensionGalleryItemViewModelFactory _galleryExtensionViewModelFactory;
private readonly IWinGetPackageManagerService? _winGetPackageManagerService;
@@ -163,7 +162,7 @@ public sealed partial class ExtensionGalleryViewModel : ObservableObject, IDispo
public ExtensionGalleryViewModel(
IExtensionGalleryService galleryService,
IEnumerable<IExtensionService> extensionServices,
IExtensionService extensionService,
ILogger<ExtensionGalleryViewModel> logger,
ExtensionGalleryItemViewModelFactory galleryExtensionViewModelFactory,
IWinGetPackageManagerService? winGetPackageManagerService = null,
@@ -172,7 +171,7 @@ public sealed partial class ExtensionGalleryViewModel : ObservableObject, IDispo
TaskScheduler? uiScheduler = null)
{
_galleryService = galleryService;
_extensionServices = extensionServices;
_extensionService = extensionService;
_logger = logger;
_galleryExtensionViewModelFactory = galleryExtensionViewModelFactory;
_winGetPackageManagerService = winGetPackageManagerService;
@@ -348,23 +347,17 @@ public sealed partial class ExtensionGalleryViewModel : ObservableObject, IDispo
List<ExtensionGalleryItemViewModel> snapshot;
try
{
var allInstalledExtensions = new List<IExtensionWrapper>();
foreach (var service in _extensionServices)
{
var extensions = refreshInstalledExtensions
? await RunInBackgroundAsync(
() => service.RefreshInstalledExtensionsAsync(includeDisabledExtensions: true),
cancellationToken)
: await RunInBackgroundAsync(
() => service.GetInstalledExtensionsAsync(includeDisabledExtensions: true),
cancellationToken);
allInstalledExtensions.AddRange(extensions);
}
var installedExtensions = refreshInstalledExtensions
? await RunInBackgroundAsync(
() => _extensionService.RefreshInstalledExtensionsAsync(includeDisabledExtensions: true),
cancellationToken)
: await RunInBackgroundAsync(
() => _extensionService.GetInstalledExtensionsAsync(includeDisabledExtensions: true),
cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
var installedPfns = new HashSet<string>(
allInstalledExtensions
installedExtensions
.Select(e => e.PackageFamilyName)
.Where(pfn => !string.IsNullOrEmpty(pfn)),
StringComparer.OrdinalIgnoreCase);

View File

@@ -1,7 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CmdPal.UI.ViewModels.Messages;
public record ProviderEnabledStateChangedMessage(string ProviderId, bool IsEnabled);

View File

@@ -2,34 +2,28 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using System.Diagnostics;
using ManagedCommon;
using Microsoft.CmdPal.Common.Services;
using Microsoft.CmdPal.UI.ViewModels.Models;
using Microsoft.CommandPalette.Extensions;
using Windows.ApplicationModel;
using Windows.ApplicationModel.AppExtensions;
using Windows.Foundation;
using Windows.Foundation.Collections;
namespace Microsoft.CmdPal.UI.ViewModels.Services;
namespace Microsoft.CmdPal.UI.ViewModels.Models;
/// <summary>
/// Extension service that manages out-of-process WinRT AppExtension-based command providers.
/// Handles package catalog monitoring, extension startup with timeouts, and background retries.
/// </summary>
public partial class WinRTExtensionService : IExtensionService, IDisposable
public partial class ExtensionService : IExtensionService, IDisposable
{
private static readonly TimeSpan ExtensionStartTimeout = TimeSpan.FromSeconds(10);
private static readonly TimeSpan BackgroundStartTimeout = TimeSpan.FromSeconds(60);
public event TypedEventHandler<IExtensionService, IEnumerable<IExtensionWrapper>>? OnExtensionAdded;
public event TypedEventHandler<IExtensionService, IEnumerable<IExtensionWrapper>>? OnExtensionRemoved;
private static readonly PackageCatalog _catalog = PackageCatalog.OpenForCurrentUser();
private static readonly Lock _lock = new();
private readonly SemaphoreSlim _getInstalledExtensionsLock = new(1, 1);
private readonly TaskScheduler _taskScheduler;
private readonly ICommandProviderCache _commandProviderCache;
private readonly SemaphoreSlim _getInstalledWidgetsLock = new(1, 1);
// private readonly ILocalSettingsService _localSettingsService;
private bool _disposedValue;
private const string CreateInstanceProperty = "CreateInstance";
@@ -38,119 +32,17 @@ public partial class WinRTExtensionService : IExtensionService, IDisposable
private static readonly List<IExtensionWrapper> _installedExtensions = [];
private static readonly List<IExtensionWrapper> _enabledExtensions = [];
public event TypedEventHandler<IExtensionService, IEnumerable<CommandProviderWrapper>>? OnProviderAdded;
public event TypedEventHandler<IExtensionService, IEnumerable<CommandProviderWrapper>>? OnProviderRemoved;
public WinRTExtensionService(TaskScheduler taskScheduler, ICommandProviderCache commandProviderCache)
public ExtensionService()
{
_taskScheduler = taskScheduler;
_commandProviderCache = commandProviderCache;
_catalog.PackageInstalling += Catalog_PackageInstalling;
_catalog.PackageUninstalling += Catalog_PackageUninstalling;
_catalog.PackageUpdating += Catalog_PackageUpdating;
}
public async Task<IEnumerable<CommandProviderWrapper>> LoadProvidersAsync(CancellationToken ct)
{
var extensions = (await GetInstalledExtensionsAsync().ConfigureAwait(false)).ToImmutableList();
var timer = Stopwatch.StartNew();
// Start all extensions in parallel
var startResults = await Task.WhenAll(extensions.Select(ext => TryStartExtensionAsync(ext, ct))).ConfigureAwait(false);
var startedWrappers = new List<CommandProviderWrapper>();
foreach (var r in startResults)
{
if (r.IsStarted)
{
startedWrappers.Add(r.Wrapper);
}
else if (r.IsTimedOut)
{
_ = StartExtensionWhenReadyAsync(r.Extension, r.PendingStartTask, r.Stopwatch, ct);
}
}
timer.Stop();
Logger.LogInfo($"WinRTExtensionService: Started {startedWrappers.Count} extension(s) in {timer.ElapsedMilliseconds} ms");
return startedWrappers;
}
public async Task SignalStopAsync()
{
var installedExtensions = await GetInstalledExtensionsAsync().ConfigureAwait(false);
foreach (var installedExtension in installedExtensions)
{
Logger.LogDebug($"Signaling dispose to {installedExtension.ExtensionUniqueId}");
try
{
if (installedExtension.IsRunning())
{
installedExtension.SignalDispose();
}
}
catch (Exception ex)
{
Logger.LogError($"Failed to send dispose signal to extension {installedExtension.ExtensionUniqueId}", ex);
}
}
}
private async Task<ExtensionStartResult> TryStartExtensionAsync(IExtensionWrapper extension, CancellationToken ct)
{
Logger.LogDebug($"Starting {extension.PackageFullName}");
var sw = Stopwatch.StartNew();
var startTask = extension.StartExtensionAsync();
try
{
await startTask.WaitAsync(ExtensionStartTimeout, ct).ConfigureAwait(false);
Logger.LogInfo($"Started extension {extension.PackageFullName} in {sw.ElapsedMilliseconds} ms");
return ExtensionStartResult.Started(extension, new CommandProviderWrapper(extension, _taskScheduler, _commandProviderCache));
}
catch (TimeoutException)
{
Logger.LogWarning($"Starting extension {extension.PackageFullName} timed out after {sw.ElapsedMilliseconds} ms, continuing in background");
return ExtensionStartResult.TimedOut(extension, startTask, sw);
}
catch (OperationCanceledException)
{
Logger.LogDebug($"Starting extension {extension.PackageFullName} was cancelled after {sw.ElapsedMilliseconds} ms");
return ExtensionStartResult.Failed(extension);
}
catch (Exception ex)
{
Logger.LogError($"Failed to start extension {extension.PackageFullName} after {sw.ElapsedMilliseconds} ms: {ex}");
return ExtensionStartResult.Failed(extension);
}
}
private async Task StartExtensionWhenReadyAsync(
IExtensionWrapper extension,
Task startTask,
Stopwatch sw,
CancellationToken ct)
{
try
{
await startTask.WaitAsync(BackgroundStartTimeout, ct).ConfigureAwait(false);
var wrapper = new CommandProviderWrapper(extension, _taskScheduler, _commandProviderCache);
Logger.LogInfo($"Late-started extension {extension.PackageFullName} in {sw.ElapsedMilliseconds} ms");
OnProviderAdded?.Invoke(this, [wrapper]);
}
catch (OperationCanceledException)
{
// Reload happened -- discard stale results
}
catch (Exception ex)
{
Logger.LogError($"Background start of extension {extension.PackageFullName} failed after {sw.ElapsedMilliseconds} ms: {ex}");
}
//// These two were an investigation into getting updates when a package
//// gets redeployed from VS. Neither get raised (nor do the above)
//// _catalog.PackageStatusChanged += Catalog_PackageStatusChanged;
//// _catalog.PackageStaging += Catalog_PackageStaging;
// _localSettingsService = settingsService;
}
private void Catalog_PackageInstalling(PackageCatalog sender, PackageInstallingEventArgs args)
@@ -181,7 +73,10 @@ public partial class WinRTExtensionService : IExtensionService, IDisposable
{
lock (_lock)
{
// Get any extension providers that we previously had from this app
UninstallPackageUnderLock(args.TargetPackage);
// then add the new ones.
InstallPackageUnderLock(args.TargetPackage);
}
}
@@ -208,25 +103,7 @@ public partial class WinRTExtensionService : IExtensionService, IDisposable
UpdateExtensionsListsFromWrappers(wrappers);
// Start extensions and notify via OnProviderAdded
var startedProviders = new List<CommandProviderWrapper>();
foreach (var wrapper in wrappers)
{
try
{
await wrapper.StartExtensionAsync().ConfigureAwait(false);
startedProviders.Add(new CommandProviderWrapper(wrapper, _taskScheduler, _commandProviderCache));
}
catch (Exception ex)
{
Logger.LogError($"Failed to start newly installed extension {wrapper.ExtensionUniqueId}: {ex}");
}
}
if (startedProviders.Count > 0)
{
OnProviderAdded?.Invoke(this, startedProviders);
}
OnExtensionAdded?.Invoke(this, wrappers);
}
finally
{
@@ -244,6 +121,7 @@ public partial class WinRTExtensionService : IExtensionService, IDisposable
if (extension.PackageFullName == package.Id.FullName)
{
CommandPaletteHost.Instance.DebugLog($"Uninstalled extension app {extension.PackageDisplayName}");
removedExtensions.Add(extension);
}
}
@@ -256,24 +134,7 @@ public partial class WinRTExtensionService : IExtensionService, IDisposable
_installedExtensions.RemoveAll(i => removedExtensions.Contains(i));
_enabledExtensions.RemoveAll(i => removedExtensions.Contains(i));
// Build placeholder wrappers for removal notification.
// The TopLevelCommandManager matches by Extension reference, so we need to pass
// something that carries the IExtensionWrapper identity.
var removedProviders = new List<CommandProviderWrapper>();
foreach (var ext in removedExtensions)
{
try
{
removedProviders.Add(new CommandProviderWrapper(ext, _taskScheduler, _commandProviderCache));
}
catch
{
// Extension may not be in a runnable state if it was uninstalled;
// we still need to signal removal
}
}
OnProviderRemoved?.Invoke(this, removedProviders);
OnExtensionRemoved?.Invoke(this, removedExtensions);
}
finally
{
@@ -282,6 +143,52 @@ public partial class WinRTExtensionService : IExtensionService, IDisposable
});
}
private static async Task<IsExtensionResult> IsValidCmdPalExtension(Package package)
{
var extensions = await AppExtensionCatalog.Open("com.microsoft.commandpalette").FindAllAsync();
foreach (var extension in extensions)
{
if (package.Id?.FullName == extension.Package?.Id?.FullName)
{
var (cmdPalProvider, classId) = await GetCmdPalExtensionPropertiesAsync(extension);
return new(cmdPalProvider is not null && classId.Count != 0, extension);
}
}
return new(false, null);
}
private static async Task<(IPropertySet? CmdPalProvider, List<string> ClassIds)> GetCmdPalExtensionPropertiesAsync(AppExtension extension)
{
var classIds = new List<string>();
var properties = await extension.GetExtensionPropertiesAsync();
if (properties is null)
{
return (null, classIds);
}
var cmdPalProvider = GetSubPropertySet(properties, "CmdPalProvider");
if (cmdPalProvider is null)
{
return (null, classIds);
}
var activation = GetSubPropertySet(cmdPalProvider, "Activation");
if (activation is null)
{
return (cmdPalProvider, classIds);
}
// Handle case where extension creates multiple instances.
classIds.AddRange(GetCreateInstanceList(activation));
return (cmdPalProvider, classIds);
}
private static async Task<IEnumerable<AppExtension>> GetInstalledAppExtensionsAsync() => await AppExtensionCatalog.Open("com.microsoft.commandpalette").FindAllAsync();
public async Task<IEnumerable<IExtensionWrapper>> GetInstalledExtensionsAsync(bool includeDisabledExtensions = false)
{
await _getInstalledExtensionsLock.WaitAsync();
@@ -308,22 +215,25 @@ public partial class WinRTExtensionService : IExtensionService, IDisposable
}
}
public IExtensionWrapper? GetInstalledExtension(string extensionUniqueId)
private static void UpdateExtensionsListsFromWrappers(List<ExtensionWrapper> wrappers)
{
var extension = _installedExtensions.Where(extension => extension.ExtensionUniqueId.Equals(extensionUniqueId, StringComparison.Ordinal));
return extension.FirstOrDefault();
}
foreach (var extensionWrapper in wrappers)
{
// var localSettingsService = Application.Current.GetService<ILocalSettingsService>();
var extensionUniqueId = extensionWrapper.ExtensionUniqueId;
var isExtensionDisabled = false; // await localSettingsService.ReadSettingAsync<bool>(extensionUniqueId + "-ExtensionDisabled");
public void EnableExtension(string extensionUniqueId)
{
var extension = _installedExtensions.Where(extension => extension.ExtensionUniqueId.Equals(extensionUniqueId, StringComparison.Ordinal));
_enabledExtensions.Add(extension.First());
}
_installedExtensions.Add(extensionWrapper);
if (!isExtensionDisabled)
{
_enabledExtensions.Add(extensionWrapper);
}
public void DisableExtension(string extensionUniqueId)
{
var extension = _enabledExtensions.Where(extension => extension.ExtensionUniqueId.Equals(extensionUniqueId, StringComparison.Ordinal));
_enabledExtensions.Remove(extension.First());
// TelemetryFactory.Get<ITelemetry>().Log(
// "Extension_ReportInstalled",
// LogLevel.Critical,
// new ReportInstalledExtensionEvent(extensionUniqueId, isEnabled: !isExtensionDisabled));
}
}
private static async Task<IEnumerable<IExtensionWrapper>> GetInstalledExtensionsAsyncUnderLock(bool includeDisabledExtensions, bool refresh)
@@ -385,8 +295,6 @@ public partial class WinRTExtensionService : IExtensionService, IDisposable
}
}
private static async Task<IEnumerable<AppExtension>> GetInstalledAppExtensionsAsync() => await AppExtensionCatalog.Open("com.microsoft.commandpalette").FindAllAsync();
private static async Task<List<ExtensionWrapper>> CreateWrappersForExtension(AppExtension extension)
{
var (cmdPalProvider, classIds) = await GetCmdPalExtensionPropertiesAsync(extension);
@@ -429,6 +337,7 @@ public partial class WinRTExtensionService : IExtensionService, IDisposable
}
else
{
// log warning that extension declared unsupported extension interface
CommandPaletteHost.Instance.DebugLog($"Extension {extension.DisplayName} declared an unsupported interface: {supportedInterface.Key}");
}
}
@@ -437,68 +346,77 @@ public partial class WinRTExtensionService : IExtensionService, IDisposable
return extensionWrapper;
}
private static void UpdateExtensionsListsFromWrappers(List<ExtensionWrapper> wrappers)
public IExtensionWrapper? GetInstalledExtension(string extensionUniqueId)
{
foreach (var extensionWrapper in wrappers)
{
var extensionUniqueId = extensionWrapper.ExtensionUniqueId;
var isExtensionDisabled = false;
var extension = _installedExtensions.Where(extension => extension.ExtensionUniqueId.Equals(extensionUniqueId, StringComparison.Ordinal));
return extension.FirstOrDefault();
}
_installedExtensions.Add(extensionWrapper);
if (!isExtensionDisabled)
public async Task SignalStopExtensionsAsync()
{
var installedExtensions = await GetInstalledExtensionsAsync();
foreach (var installedExtension in installedExtensions)
{
Logger.LogDebug($"Signaling dispose to {installedExtension.ExtensionUniqueId}");
try
{
_enabledExtensions.Add(extensionWrapper);
if (installedExtension.IsRunning())
{
installedExtension.SignalDispose();
}
}
catch (Exception ex)
{
Logger.LogError($"Failed to send dispose signal to extension {installedExtension.ExtensionUniqueId}", ex);
}
}
}
private static async Task<IsExtensionResult> IsValidCmdPalExtension(Package package)
public async Task<IEnumerable<IExtensionWrapper>> GetInstalledExtensionsAsync(ProviderType providerType, bool includeDisabledExtensions = false)
{
var extensions = await AppExtensionCatalog.Open("com.microsoft.commandpalette").FindAllAsync();
foreach (var extension in extensions)
{
if (package.Id?.FullName == extension.Package?.Id?.FullName)
{
var (cmdPalProvider, classId) = await GetCmdPalExtensionPropertiesAsync(extension);
var installedExtensions = await GetInstalledExtensionsAsync(includeDisabledExtensions);
return new(cmdPalProvider is not null && classId.Count != 0, extension);
List<IExtensionWrapper> filteredExtensions = [];
foreach (var installedExtension in installedExtensions)
{
if (installedExtension.HasProviderType(providerType))
{
filteredExtensions.Add(installedExtension);
}
}
return new(false, null);
return filteredExtensions;
}
private static async Task<(IPropertySet? CmdPalProvider, List<string> ClassIds)> GetCmdPalExtensionPropertiesAsync(AppExtension extension)
public void Dispose()
{
var classIds = new List<string>();
var properties = await extension.GetExtensionPropertiesAsync();
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
if (properties is null)
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
return (null, classIds);
if (disposing)
{
_getInstalledExtensionsLock.Dispose();
_getInstalledWidgetsLock.Dispose();
}
_disposedValue = true;
}
var cmdPalProvider = GetSubPropertySet(properties, "CmdPalProvider");
if (cmdPalProvider is null)
{
return (null, classIds);
}
var activation = GetSubPropertySet(cmdPalProvider, "Activation");
if (activation is null)
{
return (cmdPalProvider, classIds);
}
classIds.AddRange(GetCreateInstanceList(activation));
return (cmdPalProvider, classIds);
}
private static IPropertySet? GetSubPropertySet(IPropertySet propSet, string name) => propSet.TryGetValue(name, out var value) ? value as IPropertySet : null;
private static object[]? GetSubPropertySetArray(IPropertySet propSet, string name) => propSet.TryGetValue(name, out var value) ? value as object[] : null;
/// <summary>
/// There are cases where the extension creates multiple COM instances.
/// </summary>
/// <param name="activationPropSet">Activation property set object</param>
/// <returns>List of ClassId strings associated with the activation property</returns>
private static List<string> GetCreateInstanceList(IPropertySet activationPropSet)
{
var propSetList = new List<string>();
@@ -506,6 +424,8 @@ public partial class WinRTExtensionService : IExtensionService, IDisposable
if (singlePropertySet is not null)
{
var classId = GetProperty(singlePropertySet, ClassIdProperty);
// If the instance has a classId as a single string, then it's only supporting a single instance.
if (classId is not null)
{
propSetList.Add(classId);
@@ -537,24 +457,35 @@ public partial class WinRTExtensionService : IExtensionService, IDisposable
private static string? GetProperty(IPropertySet propSet, string name) => propSet[name] as string;
public void Dispose()
public void EnableExtension(string extensionUniqueId)
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
var extension = _installedExtensions.Where(extension => extension.ExtensionUniqueId.Equals(extensionUniqueId, StringComparison.Ordinal));
_enabledExtensions.Add(extension.First());
}
private void Dispose(bool disposing)
public void DisableExtension(string extensionUniqueId)
{
if (!_disposedValue)
{
if (disposing)
{
_getInstalledExtensionsLock.Dispose();
}
_disposedValue = true;
}
var extension = _enabledExtensions.Where(extension => extension.ExtensionUniqueId.Equals(extensionUniqueId, StringComparison.Ordinal));
_enabledExtensions.Remove(extension.First());
}
/*
///// <inheritdoc cref="IExtensionService.DisableExtensionIfWindowsFeatureNotAvailable(IExtensionWrapper)"/>
//public async Task<bool> DisableExtensionIfWindowsFeatureNotAvailable(IExtensionWrapper extension)
//{
// // Only attempt to disable feature if its available.
// if (IsWindowsOptionalFeatureAvailableForExtension(extension.ExtensionClassId))
// {
// return false;
// }
// _log.Warning($"Disabling extension: '{extension.ExtensionDisplayName}' because its feature is absent or unknown");
// // Remove extension from list of enabled extensions to prevent Dev Home from re-querying for this extension
// // for the rest of its process lifetime.
// DisableExtension(extension.ExtensionUniqueId);
// // Update the local settings so the next time the user launches Dev Home the extension will be disabled.
// await _localSettingsService.SaveSettingAsync(extension.ExtensionUniqueId + "-ExtensionDisabled", true);
// return true;
//} */
}
internal record struct IsExtensionResult(bool IsExtension, AppExtension? Extension)

View File

@@ -1,558 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#pragma warning disable SA1402 // File may only contain a single type - closely related adapters grouped together
#pragma warning disable SA1649 // File name should match first type name
#pragma warning disable SA1300 // Element should begin with upper-case letter (private event backing fields)
#pragma warning disable SA1516 // Elements should be separated by blank line
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text.Json;
using System.Text.Json.Nodes;
using ManagedCommon;
using Microsoft.CmdPal.UI.ViewModels.Services.JsonRpc;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Foundation;
using Windows.Storage.Streams;
namespace Microsoft.CmdPal.UI.ViewModels.Models;
/// <summary>
/// Adapts JSON command data to ICommand and IInvokableCommand interfaces.
/// </summary>
internal sealed class JSCommandAdapter : ICommand, IInvokableCommand
{
private readonly JsonElement _data;
private readonly JsonRpcConnection _connection;
private readonly Lock _eventLock = new();
private event TypedEventHandler<object, IPropChangedEventArgs>? _propChanged;
public JSCommandAdapter(JsonElement data, JsonRpcConnection connection)
{
_data = data;
_connection = connection;
}
public string Name => GetStringProperty("displayName") ?? GetStringProperty("name") ?? string.Empty;
public string Id => GetStringProperty("id") ?? string.Empty;
public IIconInfo Icon => GetIconInfo();
public event TypedEventHandler<object, IPropChangedEventArgs>? PropChanged
{
add
{
lock (_eventLock)
{
_propChanged += value;
}
}
remove
{
lock (_eventLock)
{
_propChanged -= value;
}
}
}
public void RaisePropChanged(string propertyName)
{
lock (_eventLock)
{
_propChanged?.Invoke(this, new PropChangedEventArgs(propertyName));
}
}
public ICommandResult Invoke(object sender)
{
try
{
var response = _connection.SendRequestAsync(
"command/invoke",
new JsonObject { ["commandId"] = Id },
CancellationToken.None).GetAwaiter().GetResult();
if (response.Error != null)
{
Logger.LogError($"Command invoke error: {response.Error.Message}");
return CommandResult.KeepOpen();
}
return ParseCommandResult(response.Result);
}
catch (Exception ex)
{
Logger.LogError($"Failed to invoke command {Id}: {ex.Message}");
return CommandResult.KeepOpen();
}
}
private string? GetStringProperty(string name)
{
if (_data.ValueKind == JsonValueKind.Object &&
_data.TryGetProperty(name, out var prop) && prop.ValueKind == JsonValueKind.String)
{
return prop.GetString();
}
return null;
}
private IIconInfo GetIconInfo()
{
if (_data.ValueKind == JsonValueKind.Object &&
_data.TryGetProperty("icon", out var iconProp))
{
return JSIconInfoAdapter.FromJson(iconProp);
}
return new IconInfo(string.Empty);
}
private ICommandResult ParseCommandResult(JsonElement? result)
{
return JSCommandResultAdapter.ParseCommandResult(result, _connection);
}
}
/// <summary>
/// Adapts JSON command item data to ICommandItem interface.
/// </summary>
internal sealed class JSCommandItemAdapter : ICommandItem
{
private readonly JsonElement _data;
private readonly JsonRpcConnection _connection;
private readonly Lock _eventLock = new();
private ICommand? _command;
private event TypedEventHandler<object, IPropChangedEventArgs>? _propChanged;
public JSCommandItemAdapter(JsonElement data, JsonRpcConnection connection)
{
_data = data;
_connection = connection;
}
public ICommand Command
{
get
{
if (_command == null)
{
var commandData = _data;
if (_data.ValueKind == JsonValueKind.Object &&
_data.TryGetProperty("command", out var commandElement) &&
commandElement.ValueKind == JsonValueKind.Object)
{
commandData = commandElement;
}
_command = JSCommandFactory.CreateCommandFromJson(commandData, _connection);
}
return _command;
}
}
public IContextItem[] MoreCommands => [];
public IIconInfo Icon => GetIconInfo();
public string Title => GetStringProperty("displayName") ?? GetStringProperty("title") ?? string.Empty;
public string Subtitle => GetStringProperty("description") ?? GetStringProperty("subtitle") ?? string.Empty;
public event TypedEventHandler<object, IPropChangedEventArgs>? PropChanged
{
add
{
lock (_eventLock)
{
_propChanged += value;
}
}
remove
{
lock (_eventLock)
{
_propChanged -= value;
}
}
}
public void RaisePropChanged(string propertyName)
{
lock (_eventLock)
{
_propChanged?.Invoke(this, new PropChangedEventArgs(propertyName));
}
}
private string? GetStringProperty(string name)
{
if (_data.ValueKind == JsonValueKind.Object &&
_data.TryGetProperty(name, out var prop) && prop.ValueKind == JsonValueKind.String)
{
return prop.GetString();
}
return null;
}
private IIconInfo GetIconInfo()
{
if (_data.ValueKind == JsonValueKind.Object &&
_data.TryGetProperty("icon", out var iconProp))
{
return JSIconInfoAdapter.FromJson(iconProp);
}
return new IconInfo(string.Empty);
}
}
/// <summary>
/// Utility for creating the appropriate ICommand implementation from JSON data,
/// inspecting the _type discriminator to return page proxies for pages.
/// </summary>
internal static class JSCommandFactory
{
internal static ICommand CreateCommandFromJson(JsonElement data, JsonRpcConnection connection)
{
if (data.ValueKind == JsonValueKind.Object &&
data.TryGetProperty("_type", out var typeProp) && typeProp.ValueKind == JsonValueKind.String)
{
var commandId = string.Empty;
if (data.TryGetProperty("id", out var idProp) && idProp.ValueKind == JsonValueKind.String)
{
commandId = idProp.GetString() ?? string.Empty;
}
var pageType = typeProp.GetString();
if (pageType == "dynamicListPage")
{
return new JSDynamicListPageProxy(commandId, connection, data);
}
if (pageType == "listPage")
{
return new JSListPageProxy(commandId, connection, data);
}
if (pageType == "contentPage")
{
return new JSContentPageProxy(commandId, data, connection);
}
}
return new JSCommandAdapter(data, connection);
}
}
/// <summary>
/// Adapts JSON fallback command item data to IFallbackCommandItem interface.
/// </summary>
internal sealed class JSFallbackCommandItemAdapter : IFallbackCommandItem, IFallbackCommandItem2
{
private readonly JsonElement _data;
private readonly JsonRpcConnection _connection;
private readonly Lock _eventLock = new();
private ICommand? _command;
private JSFallbackHandler? _fallbackHandler;
private string? _displayTitleOverride;
private event TypedEventHandler<object, IPropChangedEventArgs>? _propChanged;
public JSFallbackCommandItemAdapter(JsonElement data, JsonRpcConnection connection)
{
_data = data;
_connection = connection;
}
public ICommand Command
{
get
{
if (_command == null)
{
var commandData = _data;
if (_data.ValueKind == JsonValueKind.Object &&
_data.TryGetProperty("command", out var commandElement) &&
commandElement.ValueKind == JsonValueKind.Object)
{
commandData = commandElement;
}
_command = JSCommandFactory.CreateCommandFromJson(commandData, _connection);
}
return _command;
}
}
public IContextItem[] MoreCommands => [];
public IIconInfo Icon => GetIconInfo();
public string Title => GetStringProperty("displayName") ?? GetStringProperty("title") ?? string.Empty;
public string Subtitle => GetStringProperty("description") ?? GetStringProperty("subtitle") ?? string.Empty;
public string DisplayTitle => _displayTitleOverride ?? GetStringProperty("displayTitle") ?? Title;
public string Id => GetStringProperty("id") ?? string.Empty;
public void UpdateDisplayTitle(string newTitle)
{
_displayTitleOverride = newTitle;
}
public IFallbackHandler FallbackHandler
{
get
{
_fallbackHandler ??= new JSFallbackHandler(_connection, Id);
return _fallbackHandler;
}
}
public event TypedEventHandler<object, IPropChangedEventArgs>? PropChanged
{
add
{
lock (_eventLock)
{
_propChanged += value;
}
}
remove
{
lock (_eventLock)
{
_propChanged -= value;
}
}
}
public void RaisePropChanged(string propertyName)
{
lock (_eventLock)
{
_propChanged?.Invoke(this, new PropChangedEventArgs(propertyName));
}
}
private string? GetStringProperty(string name)
{
if (_data.ValueKind == JsonValueKind.Object &&
_data.TryGetProperty(name, out var prop) && prop.ValueKind == JsonValueKind.String)
{
return prop.GetString();
}
return null;
}
private IIconInfo GetIconInfo()
{
if (_data.ValueKind == JsonValueKind.Object &&
_data.TryGetProperty("icon", out var iconProp))
{
return JSIconInfoAdapter.FromJson(iconProp);
}
return new IconInfo(string.Empty);
}
private sealed class JSFallbackHandler : IFallbackHandler
{
private readonly JsonRpcConnection _connection;
private readonly string _commandId;
public JSFallbackHandler(JsonRpcConnection connection, string commandId)
{
_connection = connection;
_commandId = commandId;
}
public void UpdateQuery(string query)
{
try
{
_connection.SendNotificationAsync(
"fallback/updateQuery",
new JsonObject { ["commandId"] = _commandId, ["query"] = query },
CancellationToken.None).Wait(TimeSpan.FromSeconds(2));
}
catch (Exception ex)
{
Logger.LogWarning($"Failed to send fallback query update: {ex.Message}");
}
}
}
}
/// <summary>
/// Adapts JSON icon data to IIconInfo and IIconData interfaces.
/// </summary>
internal sealed class JSIconInfoAdapter : IIconInfo
{
private readonly JSIconDataAdapter? _light;
private readonly JSIconDataAdapter? _dark;
private JSIconInfoAdapter(JSIconDataAdapter? light, JSIconDataAdapter? dark)
{
_light = light;
_dark = dark;
}
public IIconData Light => _light ?? new JSIconDataAdapter(string.Empty, null);
public IIconData Dark => _dark ?? new JSIconDataAdapter(string.Empty, null);
public static JSIconInfoAdapter FromJson(JsonElement iconJson)
{
if (iconJson.ValueKind != JsonValueKind.Object)
{
return new JSIconInfoAdapter(null, null);
}
#pragma warning disable CA1507 // JSON property names, not C# members
JSIconDataAdapter? light = null;
JSIconDataAdapter? dark = null;
if (iconJson.TryGetProperty("Light", out var lightProp) || iconJson.TryGetProperty("light", out lightProp))
{
light = ParseIconData(lightProp);
}
if (iconJson.TryGetProperty("Dark", out var darkProp) || iconJson.TryGetProperty("dark", out darkProp))
{
dark = ParseIconData(darkProp);
}
// If only "icon"/"Icon" property exists, use it for both light and dark
if (light == null && dark == null)
{
if (iconJson.TryGetProperty("Icon", out var iconProp) || iconJson.TryGetProperty("icon", out iconProp))
{
var iconData = ParseIconData(iconProp);
light = iconData;
dark = iconData;
}
}
#pragma warning restore CA1507
return new JSIconInfoAdapter(light, dark);
}
private static JSIconDataAdapter? ParseIconData(JsonElement iconDataJson)
{
#pragma warning disable CA1507 // JSON property names, not C# members
string? iconPath = null;
string? base64Data = null;
if (iconDataJson.ValueKind == JsonValueKind.String)
{
iconPath = iconDataJson.GetString();
}
else if (iconDataJson.ValueKind == JsonValueKind.Object)
{
if ((iconDataJson.TryGetProperty("Icon", out var iconProp) || iconDataJson.TryGetProperty("icon", out iconProp)) &&
iconProp.ValueKind == JsonValueKind.String)
{
iconPath = iconProp.GetString();
}
if ((iconDataJson.TryGetProperty("Data", out var dataProp) || iconDataJson.TryGetProperty("data", out dataProp)) &&
dataProp.ValueKind == JsonValueKind.String)
{
base64Data = dataProp.GetString();
}
}
#pragma warning restore CA1507
if (!string.IsNullOrEmpty(iconPath) || !string.IsNullOrEmpty(base64Data))
{
return new JSIconDataAdapter(iconPath ?? string.Empty, base64Data);
}
return null;
}
internal sealed class JSIconDataAdapter : IIconData
{
private readonly string _icon;
private readonly string? _base64Data;
public JSIconDataAdapter(string icon, string? base64Data)
{
_icon = icon;
_base64Data = base64Data;
}
public string Icon => _icon;
public IRandomAccessStreamReference? Data
{
get
{
if (string.IsNullOrEmpty(_base64Data))
{
return null;
}
try
{
byte[] bytes;
if (_base64Data.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
{
bytes = DecodeDataUri(_base64Data);
}
else
{
bytes = Convert.FromBase64String(_base64Data);
}
var stream = new InMemoryRandomAccessStream();
stream.WriteAsync(bytes.AsBuffer()).GetResults();
stream.Seek(0);
return RandomAccessStreamReference.CreateFromStream(stream);
}
catch (Exception ex)
{
Logger.LogWarning($"Failed to decode icon data: {ex.Message}");
return null;
}
}
}
private static byte[] DecodeDataUri(string dataUri)
{
// data:[<mediatype>][;base64],<data>
var commaIndex = dataUri.IndexOf(',', StringComparison.Ordinal);
if (commaIndex < 0)
{
throw new FormatException("Invalid data URI: no comma separator");
}
var header = dataUri[..commaIndex];
var data = dataUri[(commaIndex + 1)..];
if (header.Contains(";base64", StringComparison.OrdinalIgnoreCase))
{
return Convert.FromBase64String(data);
}
// URL-encoded data (e.g., SVG)
var decoded = Uri.UnescapeDataString(data);
return System.Text.Encoding.UTF8.GetBytes(decoded);
}
}
}

View File

@@ -1,588 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#pragma warning disable SA1300 // Element should begin with upper-case letter (private event backing fields)
#pragma warning disable CS4014 // Because this call is not awaited
using System.Text.Json;
using System.Text.Json.Nodes;
using ManagedCommon;
using Microsoft.CmdPal.UI.ViewModels.Services.JsonRpc;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Foundation;
namespace Microsoft.CmdPal.UI.ViewModels.Models;
/// <summary>
/// Proxy that implements ICommandProvider by forwarding calls to a Node.js extension via JSON-RPC.
/// </summary>
public sealed class JSCommandProviderProxy : ICommandProvider, IDisposable
{
private readonly JsonRpcConnection _rpcConnection;
private readonly JSExtensionManifest _manifest;
private readonly IconInfo _defaultIcon;
private readonly Lock _eventLock = new();
private readonly Dictionary<(string Message, int State), StatusMessage> _shownStatusMessages = new();
private readonly Dictionary<string, JSFallbackCommandItemAdapter> _fallbackAdapters = new();
private IExtensionHost? _host;
private bool _isDisposed;
private ICommandSettings? _settingsCache;
private bool _settingsQueried;
private event TypedEventHandler<object, IItemsChangedEventArgs>? _itemsChanged;
public JSCommandProviderProxy(JsonRpcConnection rpcConnection, JSExtensionManifest manifest)
{
_rpcConnection = rpcConnection ?? throw new ArgumentNullException(nameof(rpcConnection));
_manifest = manifest ?? throw new ArgumentNullException(nameof(manifest));
_defaultIcon = new IconInfo(string.Empty);
RegisterNotificationHandlers();
}
public string Id => _manifest.Name ?? "unknown";
public string DisplayName => _manifest.DisplayName ?? _manifest.Name ?? "Unknown";
public IIconInfo Icon => _defaultIcon;
public event TypedEventHandler<object, IItemsChangedEventArgs>? ItemsChanged
{
add
{
lock (_eventLock)
{
_itemsChanged += value;
}
}
remove
{
lock (_eventLock)
{
_itemsChanged -= value;
}
}
}
public ICommandItem[] TopLevelCommands()
{
try
{
var response = _rpcConnection.SendRequestAsync(
"provider/getTopLevelCommands",
null,
CancellationToken.None).GetAwaiter().GetResult();
if (response.Error != null)
{
Logger.LogError($"TopLevelCommands error: {response.Error.Message}");
return [];
}
return ParseCommandItems(response.Result);
}
catch (Exception ex)
{
Logger.LogError($"Failed to get top-level commands: {ex.Message}");
return [];
}
}
public IFallbackCommandItem[]? FallbackCommands()
{
try
{
var response = _rpcConnection.SendRequestAsync(
"provider/getFallbackCommands",
null,
CancellationToken.None).GetAwaiter().GetResult();
if (response.Error != null)
{
Logger.LogWarning($"FallbackCommands error: {response.Error.Message}");
return null;
}
return ParseFallbackCommandItems(response.Result);
}
catch (Exception ex)
{
Logger.LogWarning($"Failed to get fallback commands: {ex.Message}");
return null;
}
}
public ICommand? GetCommand(string id)
{
try
{
var response = _rpcConnection.SendRequestAsync(
"provider/getCommand",
new JsonObject { ["commandId"] = id },
CancellationToken.None).GetAwaiter().GetResult();
if (response.Error != null)
{
Logger.LogWarning($"GetCommand error for {id}: {response.Error.Message}");
return null;
}
if (!response.Result.HasValue || response.Result.Value.ValueKind == JsonValueKind.Null)
{
return null;
}
var data = response.Result.Value;
// Check for page type discriminator to return the appropriate proxy
if (data.TryGetProperty("_type", out var typeProp) && typeProp.ValueKind == JsonValueKind.String)
{
var pageType = typeProp.GetString();
if (pageType == "dynamicListPage")
{
return new JSDynamicListPageProxy(id, _rpcConnection);
}
if (pageType == "listPage")
{
return new JSListPageProxy(id, _rpcConnection);
}
if (pageType == "contentPage")
{
return new JSContentPageProxy(id, _rpcConnection);
}
}
return new JSCommandAdapter(data, _rpcConnection);
}
catch (Exception ex)
{
Logger.LogWarning($"Failed to get command {id}: {ex.Message}");
return null;
}
}
public ICommandSettings? Settings
{
get
{
if (_settingsQueried)
{
return _settingsCache;
}
_settingsQueried = true;
try
{
var response = _rpcConnection.SendRequestAsync(
"provider/getSettings",
null,
CancellationToken.None).GetAwaiter().GetResult();
if (response.Error != null)
{
Logger.LogDebug($"Settings not available for {DisplayName}: {response.Error.Message}");
return null;
}
if (!response.Result.HasValue || response.Result.Value.ValueKind == JsonValueKind.Null)
{
return null;
}
var data = response.Result.Value;
var pageId = string.Empty;
if (data.TryGetProperty("id", out var idProp) && idProp.ValueKind == JsonValueKind.String)
{
pageId = idProp.GetString() ?? string.Empty;
}
if (!string.IsNullOrEmpty(pageId))
{
_settingsCache = new JSCommandSettingsProxy(pageId, _rpcConnection);
}
}
catch (Exception ex)
{
Logger.LogDebug($"Failed to get settings for {DisplayName}: {ex.Message}");
}
return _settingsCache;
}
}
public bool Frozen => true;
public void InitializeWithHost(IExtensionHost host)
{
_host = host ?? throw new ArgumentNullException(nameof(host));
Logger.LogDebug($"JSCommandProviderProxy initialized with host for {DisplayName}");
}
public void Dispose()
{
if (_isDisposed)
{
return;
}
_isDisposed = true;
lock (_eventLock)
{
_itemsChanged = null;
}
_host = null;
}
private void RegisterNotificationHandlers()
{
_rpcConnection.RegisterNotificationHandler("provider/itemsChanged", HandleItemsChangedNotification);
_rpcConnection.RegisterNotificationHandler("command/propChanged", HandleCommandPropChangedNotification);
_rpcConnection.RegisterNotificationHandler("page/propChanged", HandlePagePropChangedNotification);
_rpcConnection.RegisterNotificationHandler("content/propChanged", HandleContentPropChangedNotification);
_rpcConnection.RegisterNotificationHandler("provider/propChanged", HandleProviderPropChangedNotification);
_rpcConnection.RegisterNotificationHandler("host/logMessage", HandleLogMessageNotification);
_rpcConnection.RegisterNotificationHandler("host/showStatus", HandleShowStatusNotification);
_rpcConnection.RegisterNotificationHandler("host/hideStatus", HandleHideStatusNotification);
_rpcConnection.RegisterNotificationHandler("host/copyText", HandleCopyTextNotification);
}
private void HandleItemsChangedNotification(JsonElement paramsElement)
{
try
{
var totalItems = -1;
if (paramsElement.TryGetProperty("totalItems", out var totalItemsProp))
{
totalItems = totalItemsProp.GetInt32();
}
lock (_eventLock)
{
_itemsChanged?.Invoke(this, new ItemsChangedEventArgs(totalItems));
}
}
catch (Exception ex)
{
Logger.LogWarning($"Error handling itemsChanged notification: {ex.Message}");
}
}
private void HandleCommandPropChangedNotification(JsonElement paramsElement)
{
try
{
var commandId = string.Empty;
if (paramsElement.TryGetProperty("commandId", out var commandIdProp))
{
commandId = commandIdProp.GetString() ?? string.Empty;
}
// Update fallback adapter properties if applicable
if (!string.IsNullOrEmpty(commandId) && _fallbackAdapters.TryGetValue(commandId, out var fallbackAdapter))
{
if (paramsElement.TryGetProperty("properties", out var propsProp) && propsProp.ValueKind == JsonValueKind.Object)
{
if (propsProp.TryGetProperty("displayTitle", out var displayTitleProp) && displayTitleProp.ValueKind == JsonValueKind.String)
{
fallbackAdapter.UpdateDisplayTitle(displayTitleProp.GetString() ?? string.Empty);
}
}
fallbackAdapter.RaisePropChanged("DisplayTitle");
}
Logger.LogDebug($"[{DisplayName}] command/propChanged: commandId={commandId}");
}
catch (Exception ex)
{
Logger.LogWarning($"Error handling command/propChanged notification: {ex.Message}");
}
}
private void HandlePagePropChangedNotification(JsonElement paramsElement)
{
try
{
var pageId = string.Empty;
if (paramsElement.TryGetProperty("pageId", out var pageIdProp))
{
pageId = pageIdProp.GetString() ?? string.Empty;
}
var propertyName = string.Empty;
if (paramsElement.TryGetProperty("propertyName", out var propertyNameProp))
{
propertyName = propertyNameProp.GetString() ?? string.Empty;
}
Logger.LogDebug($"[{DisplayName}] page/propChanged: pageId={pageId}, property={propertyName}");
}
catch (Exception ex)
{
Logger.LogWarning($"Error handling page/propChanged notification: {ex.Message}");
}
}
private void HandleContentPropChangedNotification(JsonElement paramsElement)
{
try
{
var contentId = string.Empty;
if (paramsElement.TryGetProperty("contentId", out var contentIdProp))
{
contentId = contentIdProp.GetString() ?? string.Empty;
}
var propertyName = string.Empty;
if (paramsElement.TryGetProperty("propertyName", out var propertyNameProp))
{
propertyName = propertyNameProp.GetString() ?? string.Empty;
}
Logger.LogDebug($"[{DisplayName}] content/propChanged: contentId={contentId}, property={propertyName}");
}
catch (Exception ex)
{
Logger.LogWarning($"Error handling content/propChanged notification: {ex.Message}");
}
}
private void HandleProviderPropChangedNotification(JsonElement paramsElement)
{
try
{
var propertyName = string.Empty;
if (paramsElement.TryGetProperty("propertyName", out var propertyNameProp))
{
propertyName = propertyNameProp.GetString() ?? string.Empty;
}
Logger.LogDebug($"[{DisplayName}] provider/propChanged: property={propertyName}");
}
catch (Exception ex)
{
Logger.LogWarning($"Error handling provider/propChanged notification: {ex.Message}");
}
}
private void HandleLogMessageNotification(JsonElement paramsElement)
{
try
{
if (!paramsElement.TryGetProperty("message", out var messageProp))
{
return;
}
var message = messageProp.GetString() ?? string.Empty;
var state = 2; // Default to Info
if (paramsElement.TryGetProperty("state", out var stateProp))
{
state = stateProp.GetInt32();
}
// Map MessageState enum: 0=Trace, 1=Debug, 2=Info, 3=Warning, 4=Error
switch (state)
{
case 0:
case 1:
Logger.LogDebug($"[{DisplayName}] {message}");
break;
case 2:
Logger.LogInfo($"[{DisplayName}] {message}");
break;
case 3:
Logger.LogWarning($"[{DisplayName}] {message}");
break;
case 4:
Logger.LogError($"[{DisplayName}] {message}");
break;
default:
Logger.LogInfo($"[{DisplayName}] {message}");
break;
}
// Forward to host if available
if (_host != null)
{
var logMessage = new LogMessage { Message = message, State = (MessageState)state };
_host.LogMessage(logMessage);
}
}
catch (Exception ex)
{
Logger.LogWarning($"Error handling logMessage notification: {ex.Message}");
}
}
private void HandleShowStatusNotification(JsonElement paramsElement)
{
try
{
if (_host == null)
{
return;
}
if (!paramsElement.TryGetProperty("message", out var messageProp))
{
return;
}
var message = string.Empty;
var state = 2; // Default to Info
if (messageProp.ValueKind == JsonValueKind.Object)
{
if (messageProp.TryGetProperty("Message", out var msgText))
{
message = msgText.GetString() ?? string.Empty;
}
if (messageProp.TryGetProperty("State", out var msgState))
{
state = msgState.GetInt32();
}
}
else if (messageProp.ValueKind == JsonValueKind.String)
{
message = messageProp.GetString() ?? string.Empty;
}
var statusMessage = new StatusMessage
{
Message = message,
State = (MessageState)state,
};
_shownStatusMessages[(message, state)] = statusMessage;
var context = StatusContext.Extension;
if (paramsElement.TryGetProperty("context", out var contextProp))
{
if (contextProp.ValueKind == JsonValueKind.Number)
{
context = (StatusContext)contextProp.GetInt32();
}
else if (contextProp.ValueKind == JsonValueKind.String)
{
var contextStr = contextProp.GetString();
if (contextStr == "page")
{
context = StatusContext.Page;
}
}
}
_host.ShowStatus(statusMessage, context);
}
catch (Exception ex)
{
Logger.LogWarning($"Error handling showStatus notification: {ex.Message}");
}
}
private void HandleHideStatusNotification(JsonElement paramsElement)
{
try
{
if (_host == null)
{
return;
}
var message = string.Empty;
var state = 2; // Default to Info
if (paramsElement.TryGetProperty("message", out var messageProp) &&
messageProp.ValueKind == JsonValueKind.Object)
{
if (messageProp.TryGetProperty("Message", out var msgText))
{
message = msgText.GetString() ?? string.Empty;
}
if (messageProp.TryGetProperty("State", out var msgState))
{
state = msgState.GetInt32();
}
}
var key = (message, state);
if (_shownStatusMessages.TryGetValue(key, out var originalMessage))
{
_shownStatusMessages.Remove(key);
_host.HideStatus(originalMessage);
}
}
catch (Exception ex)
{
Logger.LogWarning($"Error handling hideStatus notification: {ex.Message}");
}
}
private void HandleCopyTextNotification(JsonElement paramsElement)
{
try
{
if (paramsElement.TryGetProperty("text", out var textProp) && textProp.ValueKind == JsonValueKind.String)
{
var text = textProp.GetString() ?? string.Empty;
ClipboardHelper.SetText(text);
}
}
catch (Exception ex)
{
Logger.LogWarning($"Error handling copyText notification: {ex.Message}");
}
}
private ICommandItem[] ParseCommandItems(JsonElement? result)
{
if (!result.HasValue || result.Value.ValueKind != JsonValueKind.Array)
{
return [];
}
var items = new List<ICommandItem>();
foreach (var element in result.Value.EnumerateArray())
{
items.Add(new JSCommandItemAdapter(element, _rpcConnection));
}
return items.ToArray();
}
private IFallbackCommandItem[]? ParseFallbackCommandItems(JsonElement? result)
{
if (!result.HasValue || result.Value.ValueKind != JsonValueKind.Array)
{
return null;
}
var items = new List<IFallbackCommandItem>();
foreach (var element in result.Value.EnumerateArray())
{
var adapter = new JSFallbackCommandItemAdapter(element, _rpcConnection);
items.Add(adapter);
var id = adapter.Id;
if (!string.IsNullOrEmpty(id))
{
_fallbackAdapters[id] = adapter;
}
}
return items.ToArray();
}
}

View File

@@ -1,26 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.UI.ViewModels.Services.JsonRpc;
using Microsoft.CommandPalette.Extensions;
namespace Microsoft.CmdPal.UI.ViewModels.Models;
/// <summary>
/// Proxy that implements ICommandSettings for JavaScript extensions by wrapping
/// a JSContentPageProxy for the settings page. The settings page is fetched via
/// the <c>provider/getSettings</c> JSON-RPC method and then relies on existing
/// <c>contentPage/getContent</c> and <c>form/submit</c> handlers for interaction.
/// </summary>
internal sealed class JSCommandSettingsProxy : ICommandSettings
{
private readonly JSContentPageProxy _settingsPage;
public JSCommandSettingsProxy(string settingsPageId, JsonRpcConnection connection)
{
_settingsPage = new JSContentPageProxy(settingsPageId, connection);
}
public IContentPage SettingsPage => _settingsPage;
}

Some files were not shown because too many files have changed in this diff Show More