Compare commits

...

117 Commits

Author SHA1 Message Date
khmyznikov
780abe199c add more details for API state 2026-06-16 16:43:55 -07:00
Gleb Khmyznikov
6e6e637c5a Merge branch 'main' into gleb/advanced-paste 2026-06-16 15:41:44 -07:00
Mario Hewardt
d9216f0fc7 ZoomIt - Fix race condition in audio init (#48685)
It was a race condition.
2026-06-16 15:57:13 -05:00
moooyo
8c9bd8ba64 Ignore superpowers-generated docs directory (#48633)
## Summary

Add `docs/superpowers/` to `.gitignore` so all locally generated
superpowers artifacts (specs, design docs, and plans) are not committed.

These files are produced by local AI tooling and are workspace-local
only; they should not land in the repository.

## Change

```gitignore
# Superpowers-generated docs (specs, design, plans) — local-only, not committed
docs/superpowers/
```

🤖 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>
2026-06-15 07:58:39 +00:00
Gordon Lam
aaee51b90e Upgrade Windows App SDK to 2.2.0 stable (#48546)
## Summary

Upgrades the centrally-managed Windows App SDK package versions to the
**2.2.0 stable** umbrella released on NuGet.

| Package | Before | After |
|---|---|---|
| `Microsoft.WindowsAppSDK` | 2.0.1 | **2.2.0** |
| `Microsoft.WindowsAppSDK.Foundation` | 2.0.20 | **2.1.0** |
| `Microsoft.WindowsAppSDK.AI` | 2.0.185 | **2.2.3** |
| `Microsoft.WindowsAppSDK.Runtime` | 2.0.1 | **2.2.0** |

Foundation/AI/Runtime versions match the dependency graph declared by
`Microsoft.WindowsAppSDK` `2.2.0`'s own nuspec (`Foundation=2.1.0`,
`AI=2.2.3`, `Runtime=[2.2.0]`), so transitive resolution is exact and no
version-conflict warnings are introduced.

Also bumps the CmdPal `ExtensionTemplate` sample's local
`Directory.Packages.props` so the template stays in sync with the main
repo.

## Files changed

- `Directory.Packages.props`
-
`src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props`

## Validation

Ran `tools/build/build-essentials.cmd` on a clean `origin/main`
worktree:

- `msbuild PowerToys.slnx /t:restore /p:RestorePackagesConfig=true` —
**Build succeeded, 0 warnings, 0 errors** (00:02:50)
- `src/runner/runner.vcxproj` (x64 Debug) — **Build succeeded, 0
warnings, 0 errors** (00:04:15)
- `src/settings-ui/Settings.UI/PowerToys.Settings.csproj` (x64 Debug) —
**Build succeeded, 0 warnings, 0 errors** (00:03:14)

Full module test suite has **not** been run yet — this PR only certifies
the build-essentials baseline. CI will exercise the wider build/test
matrix.

## Notes

- This is an atomic packaging-only change. No source code touched, no
behavior changes.
- If WinAppSDK 2.2.0 surfaces any runtime regression downstream, it can
be reverted as a single commit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-15 15:53:33 +08:00
moooyo
ff3c1f9252 [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>
2026-06-15 06:13:12 +00:00
thetsaw
99ee955dfb Fix DiskAnalyzer plugin link and author (#48618)
Updated the DiskAnalyzer plugin link and author information to point to
the ValleySoft organization repository instead of my personal account.

## Summary of the Pull Request

Documentation-only change. Updates the DiskAnalyzer entry in
`thirdPartyRunPlugins.md`:
- Plugin URL: `thetsaw/PowerToys.Plugin` →
`valley-soft/powertoys-diskanalyzer`
- Author: `thetsaw` → `ValleySoft`

## PR Checklist

- [ ] Closes: N/A
- [x] **Communication:** This is a minor doc-only update to correct a
repo link — no prior discussion needed
- [x] **Tests:** N/A — documentation change only
- [x] **Localization:** N/A — no user-facing strings changed
- [x] **Dev docs:** Updated `doc/thirdPartyRunPlugins.md`
- [ ] **New binaries:** N/A

## Detailed Description of the Pull Request / Additional comments

The DiskAnalyzer PowerToys Run plugin was originally submitted under my
personal GitHub account (`thetsaw`). The project has since been moved to
the official **ValleySoft** organization at:
https://github.com/valley-soft/powertoys-diskanalyzer

## Validation Steps Performed

Verified all URLs are live and resolve correctly:
- https://github.com/valley-soft/powertoys-diskanalyzer 
- https://github.com/valley-soft 
2026-06-15 04:10:13 +00:00
Gordon Lam
4d01062f76 Fix check-spelling: exclude ZoomIt rnnoise third-party tree and dedupe excludes (#48548)
## Summary

The `Check Spelling` workflow has been failing on PRs against `main`
(e.g. #48546) due to issues introduced by the recent ZoomIt webcam-blur
/ noise-cancellation change (#48266) plus two pre-existing duplicate
entries in `.github/actions/spell-check/excludes.txt`.

## What the bot reported

| Severity | Type | Location |
|---|---|---|
|  | `forbidden-pattern` (Should be `a`) |
`src/modules/ZoomIt/ZoomIt/rnnoise/kiss_fft.h:79` — third-party kiss_fft
header contains `an fft` |
| ⚠️ | `large-file` (~30 MB) |
`src/modules/ZoomIt/ZoomIt/rnnoise/rnnoise_data_little.c` |
| ⚠️ | `binary-file` |
`src/modules/ZoomIt/ZoomIt/selfie_segmentation.onnx` |
| ⚠️ | `duplicate-pattern` ×2 | `excludes.txt` lines 115/116 duplicate
lines 108/109 (`FuzzyMatcher{Comparison,Diacritics}Tests.cs`) |

## Fix

`.github/actions/spell-check/excludes.txt`:

- **Drop 2 duplicate** `FuzzyMatcher*Tests.cs` lines.
- **Add 2 new exclusions** for the new third-party ZoomIt assets:
- `^src/modules/ZoomIt/ZoomIt/rnnoise/` — entire third-party
rnnoise/kiss_fft tree (covers both the `an fft` forbidden-pattern in
`kiss_fft.h` and the 30 MB `rnnoise_data_little.c` large-file).
- `^src/modules/ZoomIt/ZoomIt/selfie_segmentation\.onnx$` — the ML model
binary.

Net change: `-2` duplicates, `+2` new exclusions → file count unchanged
at 148 lines.

## Notes

- Third-party content under `rnnoise/` should not be spell-checked; this
matches how other vendored/third-party trees in the repo are handled
(e.g. `src/common/CalculatorEngineCommon/exprtk.hpp`,
`src/common/sysinternals/Eula/`).
- No source code changes; pure config.
- Unblocks #48546 and any other PR currently failing `Check Spelling` on
`main`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-15 09:25:03 +08:00
khmyznikov
05e4076929 remove unused word 2026-06-13 14:44:14 -07:00
khmyznikov
b123a4f14a fix wording 2026-06-13 14:10:42 -07:00
khmyznikov
4ce652945c Address PR review 2026-06-13 13:58:35 -07:00
khmyznikov
2710f0c599 fix winmd collision 2026-06-12 15:17:54 -07:00
khmyznikov
f6c8550039 some knowlegde updates 2026-06-12 14:58:37 -07:00
khmyznikov
54943c943e fix missing metadata 2026-06-12 14:05:27 -07:00
Knyrps
d7d1e543ae [CmdPal][TimeDate] Open notification center when clicking the clock dock band (#48514)
## Summary

Clicking the clock dock band in the CmdPal Dock now opens the Windows
notification center (Action Center). A separate bell-icon-only dock band
is also exposed for users who prefer a dedicated notification center
shortcut.

Closes #46327

## Detail

- **Clock band left-click**: replaced the previous `NoOpCommand` on
`NowDockBand` with `OpenUrlCommand("ms-actioncenter:")`, dismissing the
Dock on invoke. The `ms-actioncenter:` URI is the correct shell
mechanism - `SendInput` Win+N was tested but dropped because it requires
foreground focus, which the Dock holds at click time.
- **Notification center band**: new `NotificationCenterDockBand`
(`ListItem`) in `TimeDateCommandsProvider.cs`, with a bell icon
(`\uEA8F`, Segoe Fluent Icons) and the same `ms-actioncenter:` command.
Exposed as a second `WrappedDockItem` from `GetDockBands()` under the id
`com.microsoft.cmdpal.timedate.notificationCenterBand`. Users can pin it
from the Dock's edit mode.
- **New resource strings**:
`timedate_show_notification_center_command_name` and
`timedate_notification_center_band_title` added to `Resources.resx` /
`Resources.Designer.cs`.
- **VS 2026 C++ build fixes** (pre-existing failures on `HEAD`): added
`_SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS` to
`CalculatorEngineCommon.vcxproj`.

## Screenshots

<img width="339" height="991" alt="image"
src="https://github.com/user-attachments/assets/e0ef8c9a-ec1f-40fa-9620-1e83e6aeeb8d"
/>

## How tested

- Built `Microsoft.CmdPal.UI.csproj` (Debug x64) - 0 errors.
- Launched dev `Microsoft.CmdPal.UI.exe`, clicked the clock band -
notification center opened correctly.
- Right-click context menu on the clock band still shows "Copy time" and
"Copy date" unchanged.
- Pinned the notification center band via edit mode - bell icon renders
icon-only, click opens notification center.
2026-06-12 19:23:46 +00:00
khmyznikov
714da1ab92 add unlock status 2026-06-12 09:42:36 -07:00
Gleb Khmyznikov
3768a9584c Merge branch 'main' into gleb/advanced-paste 2026-06-11 16:19:38 -07:00
Alex Mihaiuc
272b725ff0 Add ZoomIt webcam backgroun (blur) and microphone noise cancellation (#48266)
<!-- 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 change adds the [RNNoise](https://github.com/xiph/rnnoise) filter
for noise cancellation (audio) and the [Google
mediapipe](https://github.com/google-ai-edge/mediapipe/tree/master)
`selfie_segmentation_cpu` model for webcam background detection and
blurring.

It also fixes an issue introduced with
ba68b88ca1 causing the ZoomIt shortcuts to
fail to register in the standalone version.

<!-- 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

The Settings UI has been extended with a Noise cancellation option, a
Background selection for the webcam and a Brightness slider.

The functionality for these is added to ZoomIt itself. Also, restored
the Mono checkbox which was accidentally masked by
b93fd97e80.

<!-- 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: Mario Hewardt <marioh@microsoft.com>
2026-06-12 00:12:35 +02:00
khmyznikov
1729e26ce3 add model download trigger + winappsdk version fix 2026-06-11 14:41:40 -07:00
khmyznikov
2019d0b419 fix AI package version 2026-06-11 12:16:40 -07:00
khmyznikov
64071b7af0 add model prepare 2026-06-11 10:51:11 -07:00
khmyznikov
4d1171f7d0 fix AP location call 2026-06-11 00:10:44 -07:00
Niels Laute
7884f4217a Update readme (#48392)
<!-- 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
2026-06-11 06:54:58 +00:00
khmyznikov
ca3854744c ci: run cmdpal versioning setup last so CmdPalVersion matches stamped MSIX 2026-06-10 18:25:09 -07:00
khmyznikov
b63c319d83 Merge remote-tracking branch 'origin/main' into gleb/advanced-paste 2026-06-10 15:50:48 -07:00
khmyznikov
a7829722d3 Revert temp continueOnError on Touchdown step; service recovered 2026-06-10 15:50:40 -07:00
khmyznikov
c648980233 temp fix for resources 2026-06-10 13:59:45 -07:00
khmyznikov
a2ee84f285 fix xaml warnings 2026-06-10 11:13:18 -07:00
gilnatab
92014c81b9 [PowerDisplay] Add linked brightness control (#48207)
## Summary of the Pull Request

Adds linked brightness control to PowerDisplay so multiple
brightness-capable monitors can be controlled from a single "All
Displays" slider.

This PR:
- Adds a linked brightness mode with one master brightness slider.
- Seeds the master slider from the linked display with the lowest
Windows DISPLAY number, falling back to monitor ID for determinism.
- Persists linked mode enabled/disabled state.
- Persists per-monitor exclusions by monitor ID.
- Keeps individual display cards available under an expandable section
while linked mode is enabled.
- Shows linked-state guidance in the link icon tooltip instead of a
separate info banner.
- Allows excluded displays to keep their own independent brightness
slider.
- Keeps profiles as per-monitor snapshots; applying a profile turns
linked brightness off before applying the profile values.
- Adds unit tests for linked-brightness selection/seed behavior and
settings compatibility.

## PR Checklist

- [x] Closes: #47319
- [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

## Detailed Description of the Pull Request / Additional comments

**Screenshots**

| State | Light | Dark |
| --- | --- | --- |
| Linked mode off | <img width="519" height="817" alt="image"
src="https://github.com/user-attachments/assets/bdfae94b-b2e2-4ad3-a45c-7925bb9e5dcd"
/> | <img width="520" height="817" alt="image"
src="https://github.com/user-attachments/assets/69290a70-0375-480d-957c-c9e0af43d18e"
/> |
| Linked mode on | <img width="520" height="307" alt="image"
src="https://github.com/user-attachments/assets/a2b3572b-e51f-4bdc-9209-23ad2f96d27a"
/> | <img width="520" height="307" alt="image"
src="https://github.com/user-attachments/assets/8b14b665-b641-4256-a15b-eced82e62728"
/> |
| Linked mode on — individual displays expanded | <img width="520"
height="895" alt="image"
src="https://github.com/user-attachments/assets/0b40e60d-e78a-4814-baf6-00be7e283edd"
/> | <img width="520" height="895" alt="image"
src="https://github.com/user-attachments/assets/4f59bbfa-d6e5-4cb7-af84-cb484f922a7c"
/> |

The first version is intentionally scoped to brightness-only linked
control. Contrast, volume, color temperature, input source, and
LightSwitch-specific behavior remain independent.

Linked brightness is stored as global PowerDisplay settings:
- `linked_levels_active`
- `excluded_from_sync_monitor_ids`

Newly connected brightness-capable monitors are included by default,
because the exclusion list is the explicit exception. Hotplugging a
monitor does not immediately write brightness; linked hardware writes
happen only after the user changes the master slider.

Profiles remain per-monitor snapshots. This PR does not add
profile-level linked brightness configuration. If linked brightness is
active when a profile is applied, linked mode is turned off first, then
the saved per-monitor profile values are applied. That avoids leaving
the master linked slider active while hardware brightness has been
changed independently per monitor.

When linked mode is turned on, the master slider is seeded from the
linked brightness-capable display with the lowest Windows DISPLAY
number, falling back to monitor ID for determinism. Excluded displays
and displays without brightness support are ignored; if no linked target
remains, the master slider stays disabled. The seed only positions the
slider; it is never written to hardware, so the first user gesture is
the first broadcast.

## Validation Steps Performed

- Built `PowerDisplay.Lib.UnitTests` Debug x64:

```powershell
.\tools\build\build.ps1 -Platform x64 -Configuration Debug -Path src\modules\powerdisplay\PowerDisplay.Lib.UnitTests
```

- Ran `PowerDisplay.Lib.UnitTests` with `vstest.console.exe`
- Ran the XAML styling script:

```powershell
.\.pipelines\applyXamlStyling.ps1 -Main
```

- Result: the XAML styling script completed successfully and processed
`src/modules/powerdisplay/PowerDisplay/PowerDisplayXAML/MainWindow.xaml`.
2026-06-10 13:54:40 +08:00
khmyznikov
faf0a7cc3f return Foundation version 2026-06-09 22:44:07 -07:00
khmyznikov
aa09683d96 revert nuget config 2026-06-09 19:36:35 -07:00
khmyznikov
2572b1217b bump to 2.2.2-experimental9 2026-06-09 18:29:12 -07:00
khmyznikov
96fb1d004b docs: fix stale PRI and path references in advancedpaste.md 2026-05-28 23:25:11 -07:00
khmyznikov
046796ae3b Merge branch 'gleb/advanced-paste' of https://github.com/microsoft/PowerToys into gleb/advanced-paste 2026-05-28 23:18:49 -07:00
khmyznikov
56af1e42eb Merge branch 'gleb/advanced-paste' of https://github.com/microsoft/PowerToys into gleb/advanced-paste 2026-05-28 23:18:22 -07:00
khmyznikov
5e0f92fbf2 Merge branch 'gleb/advanced-paste' of https://github.com/microsoft/PowerToys into gleb/advanced-paste 2026-05-28 23:14:58 -07:00
khmyznikov
ccbd288a68 Merge branch 'gleb/advanced-paste' of https://github.com/microsoft/PowerToys into gleb/advanced-paste
# Conflicts:
#	.pipelines/v2/templates/steps-build-installer-vnext.yml
#	installer/PowerToysSetupVNext/CmdPal.wxs
#	installer/PowerToysSetupVNext/PowerToysInstallerVNext.wixproj
#	src/CmdPalVersion.props
#	src/common/UnitTests-CommonUtils/UnitTests-CommonUtils.vcxproj
#	tools/Verification scripts/verify-installation-script.ps1
2026-05-28 23:14:05 -07:00
khmyznikov
e9221d2174 Merge branch 'gleb/advanced-paste' of https://github.com/microsoft/PowerToys into gleb/advanced-paste
# Conflicts:
#	.pipelines/v2/templates/steps-build-installer-vnext.yml
#	installer/PowerToysSetupVNext/CmdPal.wxs
#	installer/PowerToysSetupVNext/PowerToysInstallerVNext.wixproj
#	src/CmdPalVersion.props
#	src/common/UnitTests-CommonUtils/UnitTests-CommonUtils.vcxproj
#	tools/Verification scripts/verify-installation-script.ps1
2026-05-28 23:12:03 -07:00
Gleb Khmyznikov
e7e3db830f Merge branch 'main' into gleb/advanced-paste 2026-05-28 23:09:45 -07:00
Gleb Khmyznikov
cc55315092 Merge branch 'main' into gleb/advanced-paste 2026-05-28 23:02:56 -07:00
khmyznikov
6f981528de revert change 2026-05-28 23:00:09 -07:00
khmyznikov
450568a0e8 Revert generated installer wxs files and stale build artifacts
The generateAllFileComponents.ps1 output was accidentally committed, expanding all wxs placeholder comments into hardcoded file lists. Also removes stale CmdPal BuildInfo.xml/GeneratedPackage.appxmanifest and Directory.Build.props/targets that were generated by the local build-installer.ps1 script.
2026-05-28 22:57:40 -07:00
khmyznikov
9ea621b180 Bump WindowsAppSDK.Foundation to 2.0.22 (sparse PRI fix)
Requires WindowsAppSDK PR #6376 which fixes MRT PRI lookup under sparse package identity. Without this, Application.LoadComponent hard-codes resources.pri instead of respecting ProjectPriFileName, crashing any WinUI3 app with sparse identity and a custom PRI name.
2026-05-28 22:49:46 -07:00
khmyznikov
020dd67316 Revert PRI to PowerToys.AdvancedPaste.pri: resources.pri breaks Settings
Having resources.pri in the shared WinUI3Apps directory causes Settings and QuickAccess to crash (.NET CLR exception) because MRT picks up AP's resources.pri as a fallback for other apps' resource loading. The earlier XamlParseException was actually caused by a stale/broken sparse identity registration (ExternalLocation pointing nowhere), not the PRI name — confirmed by reproducing the same crash with Settings launched from the same dev build. ImageResizer successfully uses a custom PRI name under sparse identity, so AP should too.
2026-05-20 15:59:00 -07:00
khmyznikov
c6176fb436 AdvancedPaste: revert PRI to resources.pri for XAML LoadComponent
WinUI's Application.LoadComponent hard-codes 'resources.pri' lookup under sparse identity (WinAppSDK 2.0.1). Custom PRI names work for ResourceLoader (explicit path) but not for the XAML framework's built-in ms-appx:/// URI resolution. Reverts ProjectPriFileName to resources.pri and downgrades the audit check from error to warning.
2026-05-20 14:58:19 -07:00
khmyznikov
0f7c3f70d3 AdvancedPaste: pass PRI filename to ResourceLoader explicitly
Under sparse identity, ResourceLoader() defaults to 'resources.pri' which doesn't exist (our PRI is PowerToys.AdvancedPaste.pri). This caused ERROR_MRM_MAP_NOT_FOUND (0x80073B01) crashing XAML's MeasureOverride. Match ImageResizer's pattern: new ResourceLoader('PowerToys.AdvancedPaste.pri').
2026-05-20 12:52:51 -07:00
khmyznikov
ec475f0dcc to prev 2026-05-20 11:01:22 -07:00
khmyznikov
7a3539f6d8 Revert heat-based installer: AP is flat in WinUI3Apps like ImageResizer
AP on main already outputs flat to WinUI3Apps\ and is harvested by WinUI3ApplicationsFiles. Adding sparse identity (like ImageResizer) requires zero installer changes. Reverts the heat script, Product.wxs changes, and generateAllFileComponents.ps1 churn. Only non-main installer change is CmdPal.wxs fix (unrelated) and CmdPalPackagePath/Dir wixproj defines.
2026-05-20 10:58:23 -07:00
khmyznikov
f4ddb79707 Revert flatten: restore AP subfolder layout with heat harvest + ICE03 fix
Flattening AP into WinUI3Apps\ broke the installed build because the existing flat WinUI3ApplicationsFiles harvester doesn't recurse into subdirectories. AP's XBF files (AdvancedPasteXAML\), locale satellites, and arm64\ native DLLs were missing from the MSI. Restore the WinUI3Apps\AdvancedPaste\ subfolder layout with heat-based recursive harvest (like Monaco). Three CI fixes: 1) Language='0' on all heat File entries to prevent ICE03 on gd-GB/mi-NZ/ug-CN .mui files 2) ESRPSigning paths 3) Unique PRI name (PowerToys.AdvancedPaste.pri) passes audit.
2026-05-20 10:43:24 -07:00
khmyznikov
b2251cab7f wixproj: pass CmdPalPackagePath/CmdPalPackageDir defines to WiX
CmdPal.wxs references these preprocessor vars; reverting the wixproj to main dropped them. Restore the two CmdPal-only defines (without re-introducing the AdvancedPaste heat scaffolding).
2026-05-19 20:51:27 -07:00
khmyznikov
5931b3d569 ESRPSigning_core: drop AdvancedPaste subfolder path
AP binaries now live flat in WinUI3Apps\, matching every other unpackaged WinUI3 module.
2026-05-19 17:43:17 -07:00
khmyznikov
abcb77dea1 AdvancedPaste: rename ProjectPriFileName to PowerToys.AdvancedPaste.pri
After flattening AP into WinUI3Apps\, its 'resources.pri' collided with the WinUI3Apps root convention and tripped verifyPossibleAssetConflicts.ps1. Use the same per-module naming pattern as Settings/Peek/FileLocksmith/etc. The stale comment about sparse XAML needing 'resources.pri' was wrong -- Settings already runs sparse with PowerToys.Settings.pri.
2026-05-19 16:11:05 -07:00
khmyznikov
7da7a0b277 AdvancedPaste: flatten install layout to WinUI3Apps root
AP was the only WinUI3 module shipping into a WinUI3Apps\AdvancedPaste subfolder, which forced a separate heat-based harvest in the installer. Move output to WinUI3Apps\ alongside Settings/Hosts/Peek so the existing WinUI3ApplicationsFiles harvester + dedup/CreateWinAppSDKHardlinksCA path covers AP for free. Reverts the installer scaffolding (heat script, Product.wxs/wixproj edits, generateAllFileComponents.ps1 churn, CustomAction.cpp + WinUI3Applications.wxs hardlinks rollback) so the diff is back to what main does for every other unpackaged WinUI3 app. Also drops the stale subfolder reference in verify-installation-script.ps1 and the sparse AppxManifest Executable path.
2026-05-19 15:01:09 -07:00
khmyznikov
6d1c414b07 fix vnext 2026-05-19 13:22:12 -07:00
khmyznikov
279aaf6035 fix the pipeline 2026-05-19 11:15:00 -07:00
khmyznikov
125fffc607 fix vnext 2026-05-18 22:56:32 -07:00
khmyznikov
0fcd72920d revert the CI changes 2026-05-18 19:33:46 -07:00
khmyznikov
361959374b get back to sparse 2026-05-18 16:15:34 -07:00
khmyznikov
17a0533d08 fix ap tests 2026-05-11 22:36:57 -07:00
khmyznikov
d3c8b4833c fix spelling 2026-05-11 21:25:41 -07:00
khmyznikov
832edcd580 fix vnext 2026-05-11 18:12:34 -07:00
khmyznikov
c2caa46a3f to prev 2026-05-11 15:18:45 -07:00
khmyznikov
6657da8310 move to wasdk 2.0 2026-05-11 14:55:54 -07:00
khmyznikov
79f850b0c5 Merge remote-tracking branch 'origin/main' into gleb/advanced-paste
# Conflicts:
#	Directory.Packages.props
#	src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props
2026-05-11 10:32:43 -07:00
khmyznikov
8370908d75 build fix on VS2026 2026-05-10 14:24:26 -07:00
khmyznikov
ca7bc16b7a to prev 2026-05-10 14:24:15 -07:00
khmyznikov
7416209cc7 address PR review 2026-05-09 14:57:27 -07:00
Gleb Khmyznikov
3269de6172 Merge branch 'main' into gleb/advanced-paste 2026-05-08 14:30:55 -07:00
Gleb Khmyznikov
96c5d16a45 Fix AP uninstall 2026-04-01 11:10:23 -07:00
Gleb Khmyznikov
9e02f485f0 fix debug tokens 2026-03-31 16:24:45 -07:00
Gleb Khmyznikov
822303a68c fix LAF tokens 2026-03-31 15:56:57 -07:00
Gleb Khmyznikov
68eb695626 maybe final fix? 2026-03-30 17:00:55 -07:00
Gleb Khmyznikov
d776b64a64 another thing 2026-03-30 14:41:20 -07:00
Gleb Khmyznikov
dbae67dfaa Fixing AP crash 2026-03-30 12:18:22 -07:00
Gleb Khmyznikov
011fc5a190 exclude AP from audit 2026-03-29 21:55:59 -07:00
Gleb Khmyznikov
d628d73e0b AP DisableTransitiveFrameworkReferences 2026-03-29 19:50:32 -07:00
Gleb Khmyznikov
3281c0e1a5 fix missing VCLibs 2026-03-27 23:17:18 -07:00
Gleb Khmyznikov
749378d3a2 to prev 3 2026-03-27 20:21:22 -07:00
Gleb Khmyznikov
00bc7ec822 to prev 2 2026-03-27 14:01:20 -07:00
Gleb Khmyznikov
faae1ae694 to prev 2026-03-27 09:30:40 -07:00
Gleb Khmyznikov
c43261f7cc keep fixing msix 2026-03-27 08:35:00 -07:00
Gleb Khmyznikov
ce8508ef97 fix ap vnext 2026-03-26 16:16:19 -07:00
Gleb Khmyznikov
ab9a8c979c fix ap msix search 2026-03-26 14:52:42 -07:00
Gleb Khmyznikov
9befd280a2 another try 3 2026-03-26 14:02:05 -07:00
Gleb Khmyznikov
7436b46fd6 another try 2 2026-03-26 13:18:36 -07:00
Gleb Khmyznikov
3dfeeceb7d Merge branch 'main' into gleb/advanced-paste 2026-03-26 11:16:04 -07:00
Gleb Khmyznikov
26bbfdeb8f another try 2026-03-26 11:14:23 -07:00
Gleb Khmyznikov
c46e6147b5 fix build 2026-03-26 10:32:02 -07:00
Gleb Khmyznikov
36366e13c9 fix msix 2026-03-25 16:39:34 -07:00
Gleb Khmyznikov
4fac403966 debug build 2026-03-25 15:00:15 -07:00
Gleb Khmyznikov
d28ae33c22 fix ap package 2026-03-25 13:44:14 -07:00
Gleb Khmyznikov
401ddcd95e move AP to packaged identity 2026-03-24 16:55:19 -07:00
Gleb Khmyznikov
0bc45045e0 Merge branch 'gleb/advanced-paste' of https://github.com/microsoft/PowerToys into gleb/advanced-paste 2026-03-23 21:42:10 -07:00
Gleb Khmyznikov
03438406c7 fix installer 2026-03-23 21:42:06 -07:00
gkhmyznikov
9f6d6b9cf2 Fix build 2026-03-23 16:34:54 -07:00
gkhmyznikov
f53e64a456 fix xaml 2026-03-23 15:17:27 -07:00
gkhmyznikov
0a2bc81c25 fix laf attestation string 2026-03-23 14:47:55 -07:00
Gleb Khmyznikov
fd96b463c4 Add laf as ADO secret 2026-03-21 23:26:33 -07:00
Gleb Khmyznikov
9421efe015 update readme 2026-03-21 22:44:53 -07:00
Gleb Khmyznikov
32ffd3a3b2 change workaround 2026-03-21 22:18:45 -07:00
Gleb Khmyznikov
a60eab4d03 add more params from sample 2026-03-21 14:44:46 -07:00
Gleb Khmyznikov
c2789d6080 launch hacks 2026-03-20 23:04:49 -07:00
Gleb Khmyznikov
8224062c3b add workaround 2026-03-19 17:04:56 -07:00
gkhmyznikov
dc1484549a Add LAF helper 2026-03-19 14:13:12 -07:00
gkhmyznikov
51c5d320f3 upgrade to 260317003 2026-03-19 13:50:22 -07:00
gkhmyznikov
b5fc9fea78 Revert "WinAppSdk 2.0 try"
This reverts commit a25ce4ed02.
2026-03-19 13:47:03 -07:00
gkhmyznikov
a25ce4ed02 WinAppSdk 2.0 try 2026-03-19 12:31:28 -07:00
gkhmyznikov
a3620f1d5e add settings ui manifest 2026-03-18 17:14:08 -07:00
gkhmyznikov
f8b158659e Phi Silica not tested 2026-03-17 15:15:41 -07:00
gkhmyznikov
a025d4dfc4 add settings collapse 2026-03-12 17:26:52 -07:00
gkhmyznikov
f6149bb72f default provider 2026-03-12 16:37:42 -07:00
gkhmyznikov
74eafb56b3 Custom models picker, default models picker 2026-03-12 16:13:15 -07:00
gkhmyznikov
6072ec7d8f fix settings buttons 2026-03-11 18:51:07 -07:00
gkhmyznikov
1c78950d83 more improvements 2026-03-11 18:24:34 -07:00
gkhmyznikov
ecc93eaa33 more customization 2026-03-11 15:18:51 -07:00
gkhmyznikov
eed0e20f6b fix shortcut 2026-03-10 14:44:43 -07:00
gkhmyznikov
a48e741a40 initial 2026-03-10 13:14:15 -07:00
146 changed files with 289976 additions and 836 deletions

View File

@@ -309,6 +309,11 @@ pwa
AOT
Aot
ify
LAF
Laf
languagemodel
philm
phisilica
TFM
# YML
@@ -432,3 +437,6 @@ SHELLEXPERIENCEHOST
SHELLHOST
STARTMENUEXPERIENCEHOST
WIDGETBOARD
# URIs
actioncenter

View File

@@ -1,23 +1,51 @@
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
@@ -28,109 +56,216 @@ 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
pillarbox
pfdc
pillarbox
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
@@ -138,43 +273,80 @@ 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
@@ -184,6 +356,7 @@ vminq
vmlal
vmull
vqaddq
VSHR
vshrn
vsntprintf
vsnwprintf
@@ -194,7 +367,9 @@ WAVEFORMATEXTENSIBLE
webcam
Webcam
webcams
Wextra
wfopen
WGC
wideportal
wil
WMU
@@ -202,11 +377,46 @@ 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

@@ -112,8 +112,6 @@
^src/modules/cmdpal/Microsoft\.CmdPal\.UI/Settings/InternalPage\.SampleData\.cs$
^src/modules/cmdpal/Tests/Microsoft\.CmdPal\.Common\.UnitTests/.*\.TestData\.cs$
^src/modules/cmdpal/Tests/Microsoft\.CmdPal\.Common\.UnitTests/Text/.*\.cs$
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherComparisonTests.cs$
^src/modules/cmdpal/Tests/Microsoft\.CommandPalette\.Extensions\.Toolkit\.UnitTests/FuzzyMatcherDiacriticsTests.cs$
^src/modules/colorPicker/ColorPickerUI/Shaders/GridShader\.cso$
^src/modules/launcher/Plugins/Microsoft\.PowerToys\.Run\.Plugin\.TimeDate/Properties/
^src/modules/MouseUtils/MouseJumpUI/MainForm\.resx$
@@ -137,6 +135,8 @@
^src/modules/previewpane/SvgPreviewHandler/SvgHTMLPreviewGenerator\.cs$
^src/modules/previewpane/UnitTests-MarkdownPreviewHandler/HelperFiles/MarkdownWithHTMLImageTag\.txt$
^src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/.*$
^src/modules/ZoomIt/ZoomIt/rnnoise/
^src/modules/ZoomIt/ZoomIt/selfie_segmentation\.onnx$
^src/modules/ZoomIt/ZoomIt/ZoomIt\.idc$
^src/Monaco/
^tools/project_template/ModuleTemplate/resource\.h$

View File

@@ -113,7 +113,6 @@ azman
azureaiinference
azureinference
azureopenai
Backlight
backticks
Badflags
Badmode
@@ -472,7 +471,6 @@ DWMWINDOWATTRIBUTE
DWMWINDOWMAXIMIZEDCHANGE
DWORDLONG
dworigin
DWRITE
dxgi
Dxva
eab
@@ -998,7 +996,6 @@ luid
lusrmgr
LVDS
LWA
LWIN
LZero
MAGTRANSFORM
makeappx
@@ -1208,7 +1205,6 @@ nonclient
NONCLIENTMETRICSW
NONELEVATED
nonspace
nonstd
NOOWNERZORDER
NOPARENTNOTIFY
NOPREFIX
@@ -1422,7 +1418,6 @@ Prefixer
Premul
prependpath
prepopulate
Prereq
prevhost
previewer
PREVIEWHANDLERFRAMEINFO
@@ -1607,7 +1602,6 @@ sancov
SAVEFAILED
schedtasks
SCID
SCL
Scode
SCREENFONTS
screenruler
@@ -2000,7 +1994,6 @@ valuegenerator
VARTYPE
vbcscompiler
vcamp
VCENTER
vcgtq
VCINSTALLDIR
vcp
@@ -2038,7 +2031,6 @@ vorrq
VOS
vpaddlq
vqsubq
VREDRAW
vreinterpretq
VSC
VSCBD

View File

@@ -312,3 +312,12 @@ ms-windows-store://\S+
# ANSI color codes
(?:\\(?:u00|x)1[Bb]|\\03[1-7]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+)*m
# Phi Silica internal token/ID literals
<PhiSilicaLafToken\b[^>]*>[^<]+</PhiSilicaLafToken>
\bdjwsxzxb4ksa8\b
# Special licenses text from RNNoise (BSD-style disclaimer: ``AS IS'')
``AS IS''
# Old school moniker for macOS from RNNoise
MacOS

6
.gitignore vendored
View File

@@ -19,6 +19,9 @@
[Rr]eleases/
x64/
x86/
!**/rnnoise/
!**/rnnoise/x86/
!**/rnnoise/x86/**
ARM64/
bld/
[Bb]in/
@@ -375,3 +378,6 @@ installer/*/*.wxs.bk
vcpkg_installed/
deps/vcpkg/
# Superpowers-generated docs (specs, design, plans) — local-only, not committed
docs/superpowers/

View File

@@ -102,7 +102,7 @@ extends:
useManagedIdentity: $(SigningUseManagedIdentity)
clientId: $(SigningOriginalClientId)
# Have msbuild use the release nuget config profile
additionalBuildOptions: /p:RestoreConfigFile="$(Build.SourcesDirectory)\.pipelines\release-nuget.config" /p:EnableCmdPalAOT=true
additionalBuildOptions: /p:RestoreConfigFile="$(Build.SourcesDirectory)\.pipelines\release-nuget.config" /p:EnableCmdPalAOT=true /p:PhiSilicaLafToken=$(PhiSilicaLafToken) /p:PhiSilicaLafAttestation="$(PhiSilicaLafAttestation)"
beforeBuildSteps:
# Install the Terrapin retrieval tool, which replaces vcpkg's download handler
# to redirect it to a safe Microsoft-controlled location

View File

@@ -266,6 +266,17 @@ jobs:
VCWhereExtraVersionTarget: '-prerelease'
- ${{ if eq(parameters.official, true) }}:
# M.W.T.V Setup.ps1 sets the pipeline-level XES_APPXMANIFESTVERSION env var
# from the supplied -ProjectDirectory's custom.props. Whichever invocation
# runs last wins. cmdpal MUST run last because $(CmdPalVersion) (used by the
# VNext installer and CmdPal's AppxPackageTestDir) falls back to that env
# var; if AP wins, CmdPal's folder name and MSIX filename disagree on
# VersionMinor and the installer fails with WIX0103. Each project's own
# custom.props is still applied at MSBuild time, so AP versioning is
# unaffected by the order.
- template: .\steps-setup-versioning.yml
parameters:
directory: $(build.sourcesdirectory)\src\modules\AdvancedPaste
- template: .\steps-setup-versioning.yml
parameters:
directory: $(build.sourcesdirectory)\src\modules\cmdpal

View File

@@ -3,6 +3,7 @@
<RepoRoot>$(MSBuildThisFileDirectory)</RepoRoot>
</PropertyGroup>
<Import Project="$(RepoRoot)src\Version.props" />
<Import Project="$(RepoRoot)src\PhiSilicaLaf.props" />
<PropertyGroup>
<Copyright>Copyright (C) Microsoft Corporation. All rights reserved.</Copyright>
<AssemblyCopyright>Copyright (C) Microsoft Corporation. All rights reserved.</AssemblyCopyright>

View File

@@ -78,10 +78,10 @@
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
<PackageVersion Include="Microsoft.Windows.ImplementationLibrary" Version="1.0.250325.1"/>
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6901" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="2.0.1" />
<PackageVersion Include="Microsoft.WindowsAppSDK.Foundation" Version="2.0.20" />
<PackageVersion Include="Microsoft.WindowsAppSDK.AI" Version="2.0.185" />
<PackageVersion Include="Microsoft.WindowsAppSDK.Runtime" Version="2.0.1" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="2.2.0" />
<PackageVersion Include="Microsoft.WindowsAppSDK.Foundation" Version="2.1.0" />
<PackageVersion Include="Microsoft.WindowsAppSDK.AI" Version="2.2.3" />
<PackageVersion Include="Microsoft.WindowsAppSDK.Runtime" Version="2.2.0" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageVersion Include="ModernWpfUI" Version="0.9.4" />

View File

@@ -0,0 +1,100 @@
# Advanced Paste Phi Silica local testing
How to build, register, and test **Phi Silica** in **Advanced Paste (AP)** on a dev machine,
plus the few things that actually break it.
## How it fits together
AP ships as an **unpackaged, self-contained WinUI 3 exe** (`PowerToys.AdvancedPaste.exe`).
The Windows AI `LanguageModel` (Phi Silica) API is a **Limited Access Feature (LAF)**. For it
to work, all of these must line up:
1. **Package identity** — AP runs with identity granted by the sparse MSIX
`Microsoft.PowerToys.SparseApp`.
2. **Matching LAF creds** — the token/attestation baked into the exe match the registered
sparse package's publisher.
3. **AI metadata deployed** — the `Microsoft.Windows.AI*.winmd` files ship next to the exe;
the AI runtime resolves them **at runtime**.
4. **Model ready** — supported hardware and the on-device model downloaded
(`GetReadyState() == Ready`).
Two identities — the baked token must match the registered package's publisher:
| Build | Publisher Id | LAF creds |
|-------|--------------|-----------|
| **Dev** | `djwsxzxb4ksa8` | dev default in [`src/PhiSilicaLaf.props`](../../../src/PhiSilicaLaf.props) |
| **Prod** | `8wekyb3d8bbwe` | secret, injected only by `.pipelines/v2/release.yml` |
Non-secret pairing check: the exe's baked **Attestation** must equal the registered package's
**PublisherId**.
## Build + register (dev loop)
```powershell
$repo = "X:\GitHub\PowerToys"; $Plat = "ARM64"; $Cfg = "Debug" # or x64 / Release
# Build AP only (C#; reuses existing C++ outputs):
dotnet restore "$repo\src\modules\AdvancedPaste\AdvancedPaste\AdvancedPaste.csproj" /p:Platform=$Plat
& "$repo\tools\build\build.cmd" -Path "$repo\src\modules\AdvancedPaste\AdvancedPaste" `
-Platform $Plat -Configuration $Cfg /p:BuildProjectReferences=false
# Register the dev sparse package (creates + trusts a dev cert, grants identity):
pwsh -ExecutionPolicy Bypass -File "$repo\src\PackageIdentity\BuildSparsePackage.ps1" `
-Platform $Plat -Configuration $Cfg -DevRegister
# Expect: PublisherId djwsxzxb4ksa8, IsDevelopmentMode True
```
## Check the API
`PowerToys.AdvancedPaste.exe` is a **GUI-subsystem** app — run directly in a console it prints
nothing and returns no exit code. **Redirect** stdout/stderr and wait:
```powershell
$exe = "$repo\$Plat\$Cfg\WinUI3Apps\PowerToys.AdvancedPaste.exe"
$o = "$env:TEMP\ap.out"; $e = "$env:TEMP\ap.err"
$p = Start-Process $exe '--check-phi-silica' -Wait -PassThru -WindowStyle Hidden `
-RedirectStandardOutput $o -RedirectStandardError $e
"exit=$($p.ExitCode) stdout=$((Get-Content $o -Raw).Trim())"
Get-Content $e -Raw # stderr: [phi-silica] LAF unlock status: <…>; ReadyState: <…>
```
| `--check-phi-silica` | `--prepare-phi-silica` (downloads the model) |
|----------------------|----------------------------------------------|
| `0` Available · `1` NotReady · `2` NotSupported / unlock failed | `0` Ready · `1` Failed · `2` NotSupported |
`--check` only reads state; use `--prepare` to trigger the model download (`EnsureReadyAsync`).
On failure it prints the `HRESULT` to stderr.
Confirm the running AP has identity:
```powershell
$apPid = (Get-Process PowerToys.AdvancedPaste -EA SilentlyContinue | Select-Object -First 1).Id
if ($apPid) { & "$repo\src\PackageIdentity\Check-ProcessIdentity.ps1" -ProcessId $apPid }
# Expect a PFN ending in the publisher id that matches the baked attestation
```
## What actually breaks it
- **Missing `.winmd` (most important).** The Windows AI runtime resolves
`Microsoft.Windows.AI*.winmd` from the app folder at runtime. If they aren't deployed,
`GetReadyState()` returns `NotReady` and `EnsureReadyAsync()` fails with
`RO_E_METADATA_NAME_NOT_FOUND` (`0x8000000F`) — even though identity, token, and the AI DLLs
are all correct. The build emits these winmd into `WinUI3Apps\`; the **installer must harvest
them** (`*.winmd` is in the inclusion list of
[`generateAllFileComponents.ps1`](../../../installer/PowerToysSetupVNext/generateAllFileComponents.ps1)).
Classic symptom: "works from the build output but not from the installer" → check that the
installed `WinUI3Apps\` contains `Microsoft.Windows.AI*.winmd`.
- **Dev/prod mismatch.** A dev-cred exe running against a prod sparse package (or vice versa)
makes the LAF unlock silently return `Unavailable`. Keep the exe and the registered package
the same flavor, and verify with the attestation == publisherId check above.
- **Forgot to redirect.** `--check-phi-silica` in a console prints nothing — that's the
GUI-subsystem quirk, not a result.
## Cleanup
```powershell
pwsh -ExecutionPolicy Bypass -File "$repo\src\PackageIdentity\BuildSparsePackage.ps1" -Unregister
```
⚠️ This removes any `Microsoft.PowerToys.SparseApp` registration, **including a prod one** from
an installer — reinstall/repair PowerToys to restore it.

View File

@@ -33,7 +33,81 @@ See the `ExecutePasteFormatAsync(PasteFormat, PasteActionSource)` method in `Opt
## Debugging
TODO: Add debugging information
Advanced Paste is an unpackaged, self-contained WinUI 3 app (`PowerToys.AdvancedPaste.exe`). To call Windows AI APIs (Phi Silica / `Microsoft.Windows.AI.Text.LanguageModel`) it acquires **package identity** at runtime via a shared sparse MSIX package (`Microsoft.PowerToys.SparseApp`).
### Running and attaching the debugger
1. Set the **Runner** project (`src/runner`) as the startup project in Visual Studio.
2. Launch the Runner (F5). This starts the PowerToys tray icon and loads all module interfaces.
3. Open Settings (right-click tray icon → Settings) and enable the **Advanced Paste** module if it isn't already. The module launches `PowerToys.AdvancedPaste.exe` in the background immediately.
4. In Visual Studio, go to **Debug → Attach to Process** (`Ctrl+Alt+P`) and attach to `PowerToys.AdvancedPaste.exe` (select **Managed (.NET Core)** debugger).
Alternatively, use the VS Code launch configuration **"Run AdvancedPaste"** from [.vscode/launch.json](/.vscode/launch.json) to launch the exe directly — but note that without the Runner, IPC and hotkeys won't work.
### Sparse package identity (local development)
#### Why is this needed?
- The `LanguageModel` API requires a Limited Access Feature (LAF) unlock, which only succeeds when the calling process has a matching package identity.
- Advanced Paste is an unpackaged, self-contained WinUI 3 app. The sparse package grants it identity without converting it to a full MSIX.
- The csproj uses `<ProjectPriFileName>PowerToys.AdvancedPaste.pri</ProjectPriFileName>` (matching the convention of other WinUI3 apps like ImageResizer). This requires WindowsAppSDK Foundation >= 2.0.22 ([PR #6376](https://github.com/microsoft/WindowsAppSDK/pull/6376)) which fixes MRT PRI lookup under sparse identity so `Application.LoadComponent` resolves custom-named PRI files instead of hard-coding `resources.pri`.
#### One-step dev setup
```powershell
pwsh src/PackageIdentity/BuildSparsePackage.ps1 -Platform ARM64 -Configuration Debug -DevRegister
```
`-DevRegister`:
1. Generates a dev certificate under `src/PackageIdentity/.user/` (first run only).
2. Auto-imports that certificate into `CurrentUser\TrustedPeople` and `CurrentUser\Root` so the OS grants sparse identity to AP (without trust, `GetPackageFamilyName` returns `APPMODEL_ERROR_NO_PACKAGE` and LAF unlock silently fails).
3. Removes any prior registration.
4. Rewrites the publisher in a temp copy of `AppxManifest.xml` to match the dev cert subject.
5. Registers via `Add-AppxPackage -Register … -ExternalLocation X:\…\<Platform>\<Config>\WinUI3Apps`.
After registration verify:
```powershell
$pkg = Get-AppxPackage -Name '*SparseApp*'
$pkg.PackageFamilyName # Microsoft.PowerToys.SparseApp_<PublisherId>
$pkg.PublisherId # djwsxzxb4ksa8
$pkg.IsDevelopmentMode # True
```
Confirm AP picks up sparse identity at runtime:
```powershell
& 'ARM64\Debug\WinUI3Apps\PowerToys.AdvancedPaste.exe' --check-phi-silica
# Exit 0 = Available, 1 = NotReady, 2 = NotSupported
```
Re-register after rebuilding AP, changing `src/PackageIdentity/AppxManifest.xml`, or switching platforms/configurations by re-running the same command. Unregister with `-Unregister`.
#### Troubleshooting
| Problem | Cause | Fix |
|---------|-------|-----|
| `GetPackageFamilyName` returns `APPMODEL_ERROR_NO_PACKAGE` (15700) at runtime; LAF unlock returns `Unavailable` | Dev certificate not trusted (or sparse package not registered) | Re-run `BuildSparsePackage.ps1 -DevRegister` — auto-imports the cert into `TrustedPeople` and `Root`. |
| `Microsoft.UI.Xaml.dll` crash with `0xC000027B` (class-not-registered) on AP or Settings startup | `<Application>` `Executable` path in `src/PackageIdentity/AppxManifest.xml` does not resolve under the registered `ExternalLocation` (`<Config>\WinUI3Apps\`) | Confirm every `Executable` is relative to `WinUI3Apps\` (per #47177) and the file exists under the build output. |
| AP launches but never shows a window when triggered via hotkey | Runner's pipe-server wait timed out before AP's cold-start finished bootstrapping WinAppSDK + DI host | Already mitigated by the 15 s pipe timeout in `AdvancedPasteProcessManager.cpp`; warm-start launches connect in well under 1 s. |
| `XamlParseException` / `ms-appx:///Microsoft.UI.Xaml/Themes/…` not found | WindowsAppSDK Foundation < 2.0.22; MRT can't resolve custom PRI name under sparse identity | Ensure `Microsoft.WindowsAppSDK.Foundation` >= 2.0.22 in `Directory.Packages.props`. |
### How Settings UI checks Phi Silica availability
Settings UI does not have sparse package identity. To check whether Phi Silica is available, it launches Advanced Paste as a short-lived subprocess:
```
PowerToys.AdvancedPaste.exe --check-phi-silica
```
`Program.Main` recognizes this flag, calls `PhiSilicaLafHelper.TryUnlock()` + `LanguageModel.GetReadyState()`, prints one of `Available` / `NotReady` / `NotSupported` to stdout, and exits with the matching code (0/1/2). Settings reads stdout with a 10 s wait. Because each call is a fresh process, transient `Unavailable` results are not cached across checks.
### See also
- [Phi Silica local testing & troubleshooting guide](advancedpaste-phisilica-local-testing.md) — layer-by-layer diagnostics for Phi Silica availability
- [`src/PackageIdentity/readme.md`](/src/PackageIdentity/readme.md) — full sparse package documentation
- [microsoft/microsoft-ui-xaml#10856](https://github.com/microsoft/microsoft-ui-xaml/issues/10856) — original WinUI sparse-identity PRI bug
- [microsoft/WindowsAppSDK#6376](https://github.com/microsoft/WindowsAppSDK/pull/6376) — MRT sparse PRI fix (Foundation >= 2.0.22)
## Settings

Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 KiB

After

Width:  |  Height:  |  Size: 134 KiB

View File

@@ -79,4 +79,4 @@ Below are community created plugins that target a website or software. They are
| [Linear](https://github.com/vednig/powertoys-linear) | [vednig](https://github.com/vednig) | Create Linear Issues directly from Powertoys Run |
| [PerplexitySearchShortcut](https://github.com/0x6f677548/PowerToys-Run-PerplexitySearchShortcut) | [0x6f677548](https://github.com/0x6f677548) | Search Perplexity |
| [SpeedTest](https://github.com/ruslanlap/PowerToysRun-SpeedTest) | [ruslanlap](https://github.com/ruslanlap) | One-command internet speed tests with real-time results, modern UI, and shareable links. |
| [DiskAnalyzer](https://github.com/thetsaw/PowerToys.Plugin) | [thetsaw](https://github.com/thetsaw) | Scan folders, find the largest files, and view drive space usage with visual progress bars. |
| [DiskAnalyzer](https://github.com/valley-soft/powertoys-diskanalyzer) | [ValleySoft](https://github.com/valley-soft) | Scan folders, find the largest files, and view drive space usage. |

View File

@@ -28,12 +28,23 @@ Function Generate-FileList() {
$fileExclusionList = @("*.pdb", "*.lastcodeanalysissucceeded", "createdump.exe", "powertoys.exe")
$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "srt.js", "monacoSpecialLanguages.js", "customTokenThemeRules.js", "*.pri", "*.yml")
# *.winmd: WinRT metadata for the Windows App SDK AI APIs (Phi Silica, Imaging, etc.). The AI
# runtime resolves these from the app directory at runtime, so they must ship with the product.
# Without them GetReadyState() reports NotReady and EnsureReadyAsync() fails with
# RO_E_METADATA_NAME_NOT_FOUND (0x8000000F). The build already emits them into the app output
# (e.g. WinUI3Apps); they were previously dropped here because the harvest didn't include them.
$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "srt.js", "monacoSpecialLanguages.js", "customTokenThemeRules.js", "*.pri", "*.yml", "*.winmd")
# MFC DLLs leak into the output via WindowsAppSDKSelfContained but no PowerToys binary imports them.
# Verified with dumpbin /dependents across all 2176 binaries — zero consumers.
$fileExclusionList += @("mfc140.dll", "mfc140u.dll", "mfcm140.dll", "mfcm140u.dll")
# Microsoft.CommandPalette.Extensions.winmd already has a dedicated WiX component
# (Microsoft_CommandPalette_Extensions_winmd in BaseApplications.wxs, placed in WinUI3Apps for
# CmdPal's WinRT resolution). Exclude it from the generic *.winmd harvest so it isn't declared
# by two components (WIX ICE30 "installed by two different components" breaks ref-counting).
$fileExclusionList += @("Microsoft.CommandPalette.Extensions.winmd")
$dllsToIgnore = @("System.CodeDom.dll", "WindowsBase.dll")
if ($fileDepsJson -eq [string]::Empty) {

View File

@@ -58,6 +58,16 @@
AppListEntry="none">
</uap:VisualElements>
</Application>
<Application Id="PowerToys.AdvancedPasteUI" Executable="PowerToys.AdvancedPaste.exe" EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements
DisplayName="PowerToys.AdvancedPaste"
Description="PowerToys Advanced Paste"
BackgroundColor="transparent"
Square150x150Logo="Images\Square150x150Logo.png"
Square44x44Logo="Images\Square44x44Logo.png"
AppListEntry="none">
</uap:VisualElements>
</Application>
<Application Id="PowerToys.CmdPalExtension" Executable="Microsoft.CmdPal.Ext.PowerToys.exe" EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements
DisplayName="PowerToys.CommandPaletteExtension"

View File

@@ -13,7 +13,9 @@ Param(
[switch]$Clean,
[switch]$ForceCert,
[switch]$NoSign,
[switch]$CIBuild
[switch]$CIBuild,
[switch]$DevRegister,
[switch]$Unregister
)
# PowerToys sparse packaging helper.
@@ -22,6 +24,19 @@ Param(
$ErrorActionPreference = 'Stop'
# Handle -Unregister early exit
if ($Unregister) {
$existing = Get-AppxPackage -Name 'Microsoft.PowerToys.SparseApp' -ErrorAction SilentlyContinue
if ($existing) {
Write-Host "Removing Microsoft.PowerToys.SparseApp..."
$existing | Remove-AppxPackage
Write-Host "Done."
} else {
Write-Host "Microsoft.PowerToys.SparseApp is not registered."
}
exit 0
}
$isCIBuild = $false
if ($CIBuild.IsPresent) {
$isCIBuild = $true
@@ -421,3 +436,85 @@ $winUI3AppsDir = Join-Path $outDir "WinUI3Apps"
Write-BuildLog "Register sparse package:" -Level Info
Write-BuildLog " Add-AppxPackage -Path `"$msixPath`" -ExternalLocation `"$winUI3AppsDir`"" -Level Warning
Write-BuildLog "(If already installed and you changed manifest only): Add-AppxPackage -Register `"$manifestPath`" -ExternalLocation `"$winUI3AppsDir`" -ForceApplicationShutdown" -Level Warning
# -DevRegister: automatically register the sparse package for local development
if ($DevRegister) {
Write-BuildLog "`nDev registration requested..." -Level Info
# Ensure the dev certificate is trusted (CurrentUser\TrustedPeople + CurrentUser\Root).
# Without trust, sparse identity lookup silently fails at runtime (GetPackageFamilyName
# returns APPMODEL_ERROR_NO_PACKAGE) so LAF unlocks like Phi Silica return Unavailable.
if (Test-Path $CertCerFile) {
try {
$devCert = (Get-PfxCertificate -FilePath $CertCerFile -ErrorAction Stop)
$devThumbprint = $devCert.Thumbprint
foreach ($store in @('TrustedPeople', 'Root')) {
$storePath = "Cert:\CurrentUser\$store"
$present = Get-ChildItem $storePath -ErrorAction SilentlyContinue |
Where-Object { $_.Thumbprint -eq $devThumbprint }
if (-not $present) {
Write-BuildLog "Importing dev certificate ($devThumbprint) into CurrentUser\$store" -Level Info
Import-Certificate -FilePath $CertCerFile -CertStoreLocation $storePath | Out-Null
}
}
} catch {
Write-BuildLog "Failed to import dev certificate into trust stores: $($_.Exception.Message)" -Level Warning
Write-BuildLog "Sparse identity may not be granted at runtime; LAF unlocks (e.g., Phi Silica) will fail." -Level Warning
}
} else {
Write-BuildLog "Certificate .cer file not found at $CertCerFile; skipping trust-store import." -Level Warning
}
# Remove existing registration
$existing = Get-AppxPackage -Name $script:Config.IdentityName -ErrorAction SilentlyContinue
if ($existing) {
Write-BuildLog "Removing existing registration..." -Level Info
$existing | Remove-AppxPackage
}
# Create a temp manifest with the dev publisher for -Register
$devRegDir = Join-Path ([System.IO.Path]::GetTempPath()) "PowerToysSparseDevReg"
if (Test-Path $devRegDir) { Remove-Item $devRegDir -Recurse -Force }
New-Item -ItemType Directory -Path $devRegDir -Force | Out-Null
try {
Copy-Item $manifestPath (Join-Path $devRegDir 'AppxManifest.xml')
$imagesDir = Join-Path $sparseDir 'Images'
if (Test-Path $imagesDir) {
Copy-Item $imagesDir (Join-Path $devRegDir 'Images') -Recurse
}
$devManifest = Join-Path $devRegDir 'AppxManifest.xml'
[xml]$devXml = Get-Content $devManifest -Raw
$devIdentity = $devXml.Package.Identity
if ($devIdentity.Publisher -ne $script:Config.CertSubject) {
Write-BuildLog "Rewriting publisher for dev registration" -Level Info
$devIdentity.SetAttribute('Publisher', $script:Config.CertSubject)
$devXml.Save($devManifest)
}
Write-BuildLog "Registering with ExternalLocation: $winUI3AppsDir" -Level Info
Add-AppxPackage -Register $devManifest -ExternalLocation $winUI3AppsDir
$pkg = Get-AppxPackage -Name $script:Config.IdentityName -ErrorAction SilentlyContinue
if ($pkg) {
Write-BuildLog "Dev registration successful:" -Level Success
Write-BuildLog " Publisher: $($pkg.Publisher)" -Level Info
Write-BuildLog " PublisherId: $($pkg.PublisherId)" -Level Info
Write-BuildLog " IsDevelopmentMode: $($pkg.IsDevelopmentMode)" -Level Info
Write-BuildLog " InstallLocation: $($pkg.InstallLocation)" -Level Info
if ($pkg.PublisherId -ne 'djwsxzxb4ksa8') {
Write-BuildLog "PublisherId mismatch! Expected 'djwsxzxb4ksa8', got '$($pkg.PublisherId)'. LAF unlock will fail." -Level Warning
}
} else {
Write-BuildLog "Dev registration failed — package not found." -Level Error
exit 1
}
} finally {
if (Test-Path $devRegDir) {
Remove-Item $devRegDir -Recurse -Force -ErrorAction SilentlyContinue
}
}
}

9
src/PhiSilicaLaf.props Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- Phi Silica Limited Access Feature credentials.
Local dev defaults below; overridden by /p: in release pipelines. -->
<PhiSilicaLafToken Condition="'$(PhiSilicaLafToken)'==''">RmToMMYJHZkQSrKP5lWesA==</PhiSilicaLafToken>
<PhiSilicaLafAttestation Condition="'$(PhiSilicaLafAttestation)'==''">djwsxzxb4ksa8</PhiSilicaLafAttestation>
</PropertyGroup>
</Project>

View File

@@ -73,7 +73,8 @@
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<WarningLevel>Level4</WarningLevel>
<AdditionalOptions>%(AdditionalOptions) /bigobj</AdditionalOptions>
<PreprocessorDefinitions>_WINRT_DLL;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<!-- 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>
<AdditionalIncludeDirectories>../../..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
</ClCompile>

View File

@@ -55,6 +55,22 @@ internal sealed class IntegrationTestUserSettings : IUserSettings
public IReadOnlyList<PasteFormats> AdditionalActions => _additionalActions;
public string FixSpellingAndGrammarPrompt => string.Empty;
public string FixSpellingAndGrammarSystemPrompt => string.Empty;
public string FixSpellingAndGrammarProviderId => string.Empty;
public bool FixSpellingAndGrammarCoachingEnabled => false;
public bool FixSpellingAndGrammarCoachingShortcutSet => false;
public string FixSpellingAndGrammarCoachingPrompt => string.Empty;
public string FixSpellingAndGrammarCoachingSystemPrompt => string.Empty;
public string FixSpellingAndGrammarCoachingProviderId => string.Empty;
public PasteAIConfiguration PasteAIConfiguration => _configuration;
public event EventHandler Changed;

View File

@@ -30,6 +30,16 @@ public sealed class CustomActionKernelQueryCacheServiceTests
private static readonly CacheValue TestValue = new([new(PasteFormats.PlainText, [])]);
private static readonly CacheValue TestValue2 = new([new(PasteFormats.KernelQuery, new() { { "a", "b" }, { "c", "d" } })]);
private static string LocalizeResourceId(string resourceId) => resourceId switch
{
"PasteAsPlainText" => "Paste as plain text",
"PasteAsMarkdown" => MarkdownTestKey.Prompt,
"PasteAsJson" => JSONTestKey.Prompt,
"PasteAsTxtFile" => PasteAsTxtFileKey.Prompt,
"PasteAsPngFile" => PasteAsPngFileKey.Prompt,
_ => resourceId,
};
private CustomActionKernelQueryCacheService _cacheService;
private Mock<IUserSettings> _userSettings;
private MockFileSystem _fileSystem;
@@ -41,7 +51,7 @@ public sealed class CustomActionKernelQueryCacheServiceTests
UpdateUserActions([], []);
_fileSystem = new();
_cacheService = new(_userSettings.Object, _fileSystem);
_cacheService = new(_userSettings.Object, _fileSystem, LocalizeResourceId);
}
[TestMethod]
@@ -122,7 +132,7 @@ public sealed class CustomActionKernelQueryCacheServiceTests
await _cacheService.WriteAsync(JSONTestKey, TestValue);
await _cacheService.WriteAsync(MarkdownTestKey, TestValue2);
_cacheService = new(_userSettings.Object, _fileSystem); // recreate using same mock file-system to simulate app restart
_cacheService = new(_userSettings.Object, _fileSystem, LocalizeResourceId); // recreate using same mock file-system to simulate app restart
AssertAreEqual(TestValue, _cacheService.ReadOrNull(JSONTestKey));
AssertAreEqual(TestValue2, _cacheService.ReadOrNull(MarkdownTestKey));

View File

@@ -1,5 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="$(RepoRoot)src\CmdPalVersion.props" />
<Import Project="$(RepoRoot)src\Common.Dotnet.CsWinRT.props" />
<Import Project="$(RepoRoot)src\Common.SelfContained.props" />
@@ -8,7 +9,7 @@
<OutputPath>$(RepoRoot)$(Platform)\$(Configuration)\WinUI3Apps</OutputPath>
<UseWinUI>true</UseWinUI>
<ApplicationIcon>Assets\AdvancedPaste\AdvancedPaste.ico</ApplicationIcon>
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationManifest>AdvancedPaste.dev.manifest</ApplicationManifest>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
@@ -20,9 +21,13 @@
<RootNamespace>AdvancedPaste</RootNamespace>
<DisableWinExeOutputInference>true</DisableWinExeOutputInference>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
<ProjectPriFileName>PowerToys.AdvancedPaste.pri</ProjectPriFileName>
<DefineConstants>DISABLE_XAML_GENERATED_MAIN,TRACE</DefineConstants>
<Version>$(AdvancedPasteVersion)</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(CIBuild)'=='true'">
<ApplicationManifest>AdvancedPaste.prod.manifest</ApplicationManifest>
</PropertyGroup>
<!-- See https://learn.microsoft.com/windows/apps/develop/platform/csharp-winrt/net-projection-from-cppwinrt-component for more info -->
@@ -32,6 +37,25 @@
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
</PropertyGroup>
<!-- BODGY: XES Versioning and WinAppSDK get into a fight about the app manifest, which breaks WinAppSDK. -->
<Target Name="RearrangeXefVersioningAndWinAppSDKResourceGeneration" DependsOnTargets="GetNewAppManifestValues;CreateWinRTRegistration" BeforeTargets="XesWriteVersionInfoResourceFile">
<PropertyGroup>
<OriginalApplicationManifest>$(ApplicationManifest.Replace("$(MSBuildProjectDirectory)\",""))</OriginalApplicationManifest>
</PropertyGroup>
<Message Importance="High" Text="Updated final manifest path to $(OriginalApplicationManifest)" />
</Target>
<!-- Generate PhiSilicaLafCredentials from MSBuild properties (PhiSilicaLaf.props / CI overrides). -->
<Target Name="GeneratePhiSilicaLafCredentials" BeforeTargets="BeforeCompile;CoreCompile;XamlPreCompile">
<PropertyGroup>
<PhiSilicaLafCredentialsFile>$(IntermediateOutputPath)PhiSilicaLafCredentials.g.cs</PhiSilicaLafCredentialsFile>
</PropertyGroup>
<WriteLinesToFile File="$(PhiSilicaLafCredentialsFile)" Overwrite="true" Lines="// &lt;auto-generated/&gt;;namespace AdvancedPaste%3B;;internal static class PhiSilicaLafCredentials;{; internal const string Token = &quot;$(PhiSilicaLafToken)&quot;%3B; internal const string Attestation = &quot;$(PhiSilicaLafAttestation)&quot;%3B;}" />
<ItemGroup>
<Compile Include="$(PhiSilicaLafCredentialsFile)" Condition="!@(Compile->AnyHaveMetadataValue('Identity', '$(PhiSilicaLafCredentialsFile)'))" />
</ItemGroup>
</Target>
<ItemGroup>
<None Remove="AdvancedPasteXAML\Controls\ClipboardHistoryItemPreviewControl.xaml" />
<None Remove="AdvancedPasteXAML\Controls\PromptBox.xaml" />
@@ -66,9 +90,9 @@
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="Microsoft.SemanticKernel" />
<PackageReference Include="Microsoft.WindowsAppSDK" />
<PackageReference Include="Microsoft.WindowsAppSDK.AI" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
<PackageReference Include="System.ClientModel" />
<PackageReference Include="Microsoft.Windows.Compatibility" />
<PackageReference Include="Microsoft.Windows.CsWin32" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
<PackageReference Include="ReverseMarkdown" />
@@ -85,7 +109,8 @@
<!-- TODO: fix issues and reenable -->
<!-- These are caused by streamjsonrpc dependency on Microsoft.VisualStudio.Threading.Analyzers -->
<!-- We might want to add that to the project and fix the issues as well -->
<NoWarn>VSTHRD002;VSTHRD110;VSTHRD100;VSTHRD200;VSTHRD101</NoWarn>
<!-- CS8305: generated XamlTypeInfo references experimental ItemsView enums in WinAppSDK 2.2.x-experimental. -->
<NoWarn>VSTHRD002;VSTHRD110;VSTHRD100;VSTHRD200;VSTHRD101;CS8305</NoWarn>
</PropertyGroup>
<!--
@@ -97,6 +122,10 @@
<ProjectCapability Include="Msix" />
</ItemGroup>
<ItemGroup>
<SDKReference Include="Microsoft.VCLibs.Desktop, Version=14.0" />
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>AdvancedPaste.UnitTests</_Parameter1>
@@ -104,8 +133,6 @@
</ItemGroup>
<ItemGroup>
<!-- HACK: Common.UI is referenced, even if it is not used, to force dll versions to be the same as in other projects that use it. It's still unclear why this is the case, but this is need for flattening the install directory. -->
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\..\common\LanguageModelProvider\LanguageModelProvider.csproj" />
<ProjectReference Include="..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
@@ -154,4 +181,5 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0"
xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="AdvancedPaste.app"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect:
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update
-->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
<msix xmlns="urn:schemas-microsoft-com:msix.v1"
publisher="CN=PowerToys Dev, O=PowerToys, L=Redmond, S=Washington, C=US"
packageName="Microsoft.PowerToys.SparseApp"
applicationId="PowerToys.AdvancedPasteUI" />
</assembly>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0"
xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="AdvancedPaste.app"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect:
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update
-->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
<msix xmlns="urn:schemas-microsoft-com:msix.v1"
publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
packageName="Microsoft.PowerToys.SparseApp"
applicationId="PowerToys.AdvancedPasteUI" />
</assembly>

View File

@@ -188,14 +188,23 @@ namespace AdvancedPaste
}
else
{
if (!AdditionalActionIPCKeys.TryGetValue(messageParts[1], out PasteFormats pasteFormat))
const string coachingSuffix = "-coaching";
var actionKey = messageParts[1];
bool forceCoaching = actionKey.EndsWith(coachingSuffix, StringComparison.OrdinalIgnoreCase);
if (forceCoaching)
{
actionKey = actionKey[..^coachingSuffix.Length];
}
if (!AdditionalActionIPCKeys.TryGetValue(actionKey, out PasteFormats pasteFormat))
{
Logger.LogWarning($"Unexpected additional action type {messageParts[1]}");
}
else
{
await ShowWindow();
await viewModel.ExecutePasteFormatAsync(pasteFormat, PasteActionSource.GlobalKeyboardShortcut);
await viewModel.ExecutePasteFormatAsync(pasteFormat, PasteActionSource.GlobalKeyboardShortcut, forceCoaching);
}
}
}

View File

@@ -382,6 +382,7 @@
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid
@@ -438,13 +439,32 @@
TextWrapping="Wrap" />
</ScrollViewer>
</Grid>
<Rectangle
<StackPanel
Grid.Row="1"
Padding="12,8,12,8"
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
Spacing="4"
Visibility="{x:Bind ViewModel.HasCoachingExplanation, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<TextBlock
x:Uid="CoachingExplanationTitle"
FontWeight="SemiBold"
Style="{StaticResource CaptionTextBlockStyle}" />
<ScrollViewer MaxHeight="200">
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind ViewModel.CoachingExplanation, Mode=OneWay}"
TextWrapping="Wrap" />
</ScrollViewer>
</StackPanel>
<Rectangle
Grid.Row="2"
Height="1"
Fill="{ThemeResource DividerStrokeColorDefaultBrush}" />
<Grid
Grid.Row="2"
Grid.Row="3"
Margin="12"
ColumnSpacing="8">
<Grid.ColumnDefinitions>

View File

@@ -25,6 +25,22 @@ namespace AdvancedPaste.Settings
public IReadOnlyList<PasteFormats> AdditionalActions { get; }
public string FixSpellingAndGrammarPrompt { get; }
public string FixSpellingAndGrammarSystemPrompt { get; }
public string FixSpellingAndGrammarProviderId { get; }
public bool FixSpellingAndGrammarCoachingEnabled { get; }
public bool FixSpellingAndGrammarCoachingShortcutSet { get; }
public string FixSpellingAndGrammarCoachingPrompt { get; }
public string FixSpellingAndGrammarCoachingSystemPrompt { get; }
public string FixSpellingAndGrammarCoachingProviderId { get; }
public PasteAIConfiguration PasteAIConfiguration { get; }
public event EventHandler Changed;

View File

@@ -157,8 +157,6 @@ namespace AdvancedPaste.Helpers
{
public int X;
public int Y;
public static explicit operator System.Windows.Point(PointInter point) => new System.Windows.Point(point.X, point.Y);
}
[DllImport("user32.dll")]

View File

@@ -46,6 +46,22 @@ namespace AdvancedPaste.Settings
public IReadOnlyList<AdvancedPasteCustomAction> CustomActions => _customActions;
public string FixSpellingAndGrammarPrompt { get; private set; } = string.Empty;
public string FixSpellingAndGrammarSystemPrompt { get; private set; } = string.Empty;
public string FixSpellingAndGrammarProviderId { get; private set; } = string.Empty;
public bool FixSpellingAndGrammarCoachingEnabled { get; private set; }
public bool FixSpellingAndGrammarCoachingShortcutSet { get; private set; }
public string FixSpellingAndGrammarCoachingPrompt { get; private set; } = string.Empty;
public string FixSpellingAndGrammarCoachingSystemPrompt { get; private set; } = string.Empty;
public string FixSpellingAndGrammarCoachingProviderId { get; private set; } = string.Empty;
public PasteAIConfiguration PasteAIConfiguration { get; private set; }
public UserSettings(IFileSystem fileSystem)
@@ -113,10 +129,21 @@ namespace AdvancedPaste.Settings
EnableClipboardPreview = properties.EnableClipboardPreview;
PasteAIConfiguration = properties.PasteAIConfiguration ?? new PasteAIConfiguration();
var fixSpellingAction = properties.AdditionalActions.FixSpellingAndGrammar;
FixSpellingAndGrammarPrompt = fixSpellingAction.Prompt ?? string.Empty;
FixSpellingAndGrammarSystemPrompt = fixSpellingAction.SystemPrompt ?? string.Empty;
FixSpellingAndGrammarProviderId = fixSpellingAction.ProviderId ?? string.Empty;
FixSpellingAndGrammarCoachingEnabled = fixSpellingAction.CoachingEnabled;
FixSpellingAndGrammarCoachingShortcutSet = fixSpellingAction.CoachingShortcut?.Code > 0;
FixSpellingAndGrammarCoachingPrompt = fixSpellingAction.CoachingPrompt ?? string.Empty;
FixSpellingAndGrammarCoachingSystemPrompt = fixSpellingAction.CoachingSystemPrompt ?? string.Empty;
FixSpellingAndGrammarCoachingProviderId = fixSpellingAction.CoachingProviderId ?? string.Empty;
var sourceAdditionalActions = properties.AdditionalActions;
(PasteFormats Format, IAdvancedPasteAction[] Actions)[] additionalActionFormats =
[
(PasteFormats.ImageToText, [sourceAdditionalActions.ImageToText]),
(PasteFormats.FixSpellingAndGrammar, [sourceAdditionalActions.FixSpellingAndGrammar]),
(PasteFormats.PasteAsTxtFile, [sourceAdditionalActions.PasteAsFile, sourceAdditionalActions.PasteAsFile.PasteAsTxtFile]),
(PasteFormats.PasteAsPngFile, [sourceAdditionalActions.PasteAsFile, sourceAdditionalActions.PasteAsFile.PasteAsPngFile]),
(PasteFormats.PasteAsHtmlFile, [sourceAdditionalActions.PasteAsFile, sourceAdditionalActions.PasteAsFile.PasteAsHtmlFile]),

View File

@@ -24,20 +24,22 @@ public sealed class PasteFormat
IsEnabled = SupportsClipboardFormats(clipboardFormats) && (isAIServiceEnabled || !Metadata.RequiresAIService);
}
public static PasteFormat CreateStandardFormat(PasteFormats format, ClipboardFormat clipboardFormats, bool isAIServiceEnabled, Func<string, string> resourceLoader) =>
public static PasteFormat CreateStandardFormat(PasteFormats format, ClipboardFormat clipboardFormats, bool isAIServiceEnabled, Func<string, string> resourceLoader, string providerId = null) =>
new(format, clipboardFormats, isAIServiceEnabled)
{
Name = MetadataDict[format].ResourceId == null ? string.Empty : resourceLoader(MetadataDict[format].ResourceId),
Prompt = string.Empty,
IsSavedQuery = false,
ProviderId = providerId ?? string.Empty,
};
public static PasteFormat CreateCustomAIFormat(PasteFormats format, string name, string prompt, bool isSavedQuery, ClipboardFormat clipboardFormats, bool isAIServiceEnabled) =>
public static PasteFormat CreateCustomAIFormat(PasteFormats format, string name, string prompt, bool isSavedQuery, ClipboardFormat clipboardFormats, bool isAIServiceEnabled, string providerId = null) =>
new(format, clipboardFormats, isAIServiceEnabled)
{
Name = name,
Prompt = prompt,
IsSavedQuery = isSavedQuery,
ProviderId = providerId ?? string.Empty,
};
public PasteFormatMetadataAttribute Metadata => MetadataDict[Format];
@@ -50,6 +52,8 @@ public sealed class PasteFormat
public string Prompt { get; private init; }
public string ProviderId { get; private init; } = string.Empty;
public bool IsSavedQuery { get; private init; }
public bool IsEnabled { get; private init; }

View File

@@ -38,6 +38,17 @@ public enum PasteFormats
KernelFunctionDescription = "Takes clipboard text and formats it as JSON text.")]
Json,
[PasteFormatMetadata(
IsCoreAction = false,
ResourceId = "FixSpellingAndGrammar",
IconGlyph = "\uE8E2",
RequiresAIService = true,
CanPreview = true,
SupportedClipboardFormats = ClipboardFormat.Text,
IPCKey = AdvancedPasteAdditionalActions.PropertyNames.FixSpellingAndGrammar,
KernelFunctionDescription = "Fixes all spelling and grammar errors in the clipboard text and returns the corrected version.")]
FixSpellingAndGrammar,
[PasteFormatMetadata(
IsCoreAction = false,
ResourceId = "ImageToText",

View File

@@ -0,0 +1,67 @@
// 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;
using System.Diagnostics;
using Windows.ApplicationModel;
namespace AdvancedPaste;
internal static class PhiSilicaLafHelper
{
private const string FeatureId = "com.microsoft.windows.ai.languagemodel";
private static readonly object _lock = new();
private static bool _unlocked;
/// <summary>
/// Gets the status of the most recent <see cref="TryUnlock"/> attempt
/// (e.g. Available, AvailableWithoutToken, Unavailable, or "Exception: ...").
/// Exposed so callers can surface the real LAF result for diagnostics; the
/// generic "Access is denied" from downstream model calls does not reveal it.
/// </summary>
public static string LastUnlockStatus { get; private set; } = "NotAttempted";
public static bool TryUnlock()
{
// Only cache a successful unlock. Negative results (Unavailable, Unknown, exceptions)
// are often transient — e.g., AI feature stack not yet initialized after sign-in or
// sparse identity not fully applied to a freshly-started process — and retrying on
// the next call lets AP recover without restart.
if (_unlocked)
{
return true;
}
lock (_lock)
{
if (_unlocked)
{
return true;
}
try
{
var access = LimitedAccessFeatures.TryUnlockFeature(
FeatureId,
PhiSilicaLafCredentials.Token,
PhiSilicaLafCredentials.Attestation + " has registered their use of com.microsoft.windows.ai.languagemodel with Microsoft and agrees to the terms of use.");
_unlocked = access.Status == LimitedAccessFeatureStatus.Available
|| access.Status == LimitedAccessFeatureStatus.AvailableWithoutToken;
LastUnlockStatus = access.Status.ToString();
Debug.WriteLine($"Phi Silica LAF unlock status: {access.Status}");
}
catch (Exception ex)
{
LastUnlockStatus = "Exception: " + ex.Message;
Debug.WriteLine($"Phi Silica LAF unlock failed: {ex.Message}");
_unlocked = false;
}
return _unlocked;
}
}
}

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Linq;
using System.Threading;
using ManagedCommon;
@@ -14,16 +15,26 @@ namespace AdvancedPaste
public static class Program
{
[STAThread]
public static void Main(string[] args)
public static int Main(string[] args)
{
Logger.InitializeLogger("\\AdvancedPaste\\Logs");
WinRT.ComWrappersSupport.InitializeComWrappers();
if (args.Contains("--check-phi-silica", StringComparer.OrdinalIgnoreCase))
{
return CheckPhiSilicaAvailability();
}
if (args.Contains("--prepare-phi-silica", StringComparer.OrdinalIgnoreCase))
{
return PreparePhiSilica();
}
if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredAdvancedPasteEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
{
Logger.LogWarning("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
return;
return 1;
}
var instanceKey = AppInstance.FindOrRegisterForKey("PowerToys_AdvancedPaste_Instance");
@@ -41,6 +52,100 @@ namespace AdvancedPaste
{
Logger.LogWarning("Another instance of AdvancedPasteUI is running. Exiting.");
}
return 0;
}
/// <summary>
/// Checks Phi Silica availability without starting the WinUI app.
/// Used by Settings UI to probe API status via subprocess.
/// Exit codes: 0 = available, 1 = not ready (model needs download), 2 = not supported or error.
/// </summary>
private static int CheckPhiSilicaAvailability()
{
try
{
PhiSilicaLafHelper.TryUnlock();
var readyState = Microsoft.Windows.AI.Text.LanguageModel.GetReadyState();
Console.Error.WriteLine($"[phi-silica] LAF unlock status: {PhiSilicaLafHelper.LastUnlockStatus}; ReadyState: {readyState}");
switch (readyState)
{
case Microsoft.Windows.AI.AIFeatureReadyState.Ready:
Console.Out.WriteLine("Available");
return 0;
case Microsoft.Windows.AI.AIFeatureReadyState.NotReady:
Console.Out.WriteLine("NotReady");
return 1;
default:
// NotSupportedOnCurrentSystem, DisabledByUser, CapabilityMissing,
// NotCompatibleWithSystemHardware, OSUpdateNeeded, or any future state:
// the model isn't usable and "Download model" (EnsureReadyAsync) won't fix it.
// CapabilityMissing in particular means the systemAIModels capability isn't
// authorized for the app, so EnsureReadyAsync throws E_ACCESSDENIED (0x80070005).
Console.Out.WriteLine("NotSupported");
return 2;
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
Console.Out.WriteLine("NotSupported");
return 2;
}
}
/// <summary>
/// Triggers Phi Silica model preparation (download) without starting the WinUI app.
/// Moves the model from NotReady to Ready by calling EnsureReadyAsync.
/// Exit codes: 0 = ready, 1 = preparation failed, 2 = not supported or error.
/// </summary>
private static int PreparePhiSilica()
{
try
{
PhiSilicaLafHelper.TryUnlock();
var readyState = Microsoft.Windows.AI.Text.LanguageModel.GetReadyState();
Console.Error.WriteLine($"[phi-silica] LAF unlock status: {PhiSilicaLafHelper.LastUnlockStatus}; ReadyState: {readyState}");
if (readyState is Microsoft.Windows.AI.AIFeatureReadyState.NotSupportedOnCurrentSystem
or Microsoft.Windows.AI.AIFeatureReadyState.DisabledByUser)
{
Console.Out.WriteLine("NotSupported");
return 2;
}
if (readyState == Microsoft.Windows.AI.AIFeatureReadyState.Ready)
{
Console.Out.WriteLine("Ready");
return 0;
}
// Run on a thread-pool (MTA) thread: the WinRT async operation does not
// marshal correctly when blocked on from the [STAThread] entry point.
var result = System.Threading.Tasks.Task.Run(
() => Microsoft.Windows.AI.Text.LanguageModel.EnsureReadyAsync().AsTask()).GetAwaiter().GetResult();
if (result.Status != Microsoft.Windows.AI.AIFeatureReadyResultState.Success)
{
int hresult = result.ExtendedError?.HResult ?? 0;
Console.Error.WriteLine($"[phi-silica] EnsureReadyAsync Status: {result.Status}; HRESULT: 0x{hresult:X8}; Message: {result.ExtendedError?.Message}");
Console.Error.WriteLine(result.ExtendedError?.Message ?? result.Status.ToString());
Console.Out.WriteLine("Failed");
return 1;
}
Console.Out.WriteLine("Ready");
return 0;
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
Console.Out.WriteLine("NotSupported");
return 2;
}
}
}
}

View File

@@ -33,14 +33,21 @@ public sealed class CustomActionKernelQueryCacheService : IKernelQueryCacheServi
private readonly IUserSettings _userSettings;
private readonly IFileSystem _fileSystem;
private readonly SettingsUtils _settingsUtil;
private readonly Func<string, string> _getLocalizedString;
private static string Version => Assembly.GetExecutingAssembly()?.GetName()?.Version?.ToString() ?? string.Empty;
public CustomActionKernelQueryCacheService(IUserSettings userSettings, IFileSystem fileSystem)
: this(userSettings, fileSystem, ResourceLoaderInstance.ResourceLoader.GetString)
{
}
internal CustomActionKernelQueryCacheService(IUserSettings userSettings, IFileSystem fileSystem, Func<string, string> getLocalizedString)
{
_userSettings = userSettings;
_fileSystem = fileSystem;
_settingsUtil = new SettingsUtils(fileSystem);
_getLocalizedString = getLocalizedString;
_userSettings.Changed += OnUserSettingsChanged;
@@ -112,7 +119,7 @@ public sealed class CustomActionKernelQueryCacheService : IKernelQueryCacheServi
let metadata = pair.Value
where !string.IsNullOrEmpty(metadata.ResourceId)
where metadata.IsCoreAction || _userSettings.AdditionalActions.Contains(format)
select ResourceLoaderInstance.ResourceLoader.GetString(metadata.ResourceId);
select _getLocalizedString(metadata.ResourceId);
var customActionPrompts = from customAction in _userSettings.CustomActions
select customAction.Prompt;

View File

@@ -40,10 +40,15 @@ namespace AdvancedPaste.Services.CustomActions
this.userSettings = userSettings;
}
public async Task<CustomActionTransformResult> TransformAsync(string prompt, string inputText, byte[] imageBytes, CancellationToken cancellationToken, IProgress<double> progress)
public async Task<CustomActionTransformResult> TransformAsync(string prompt, string inputText, byte[] imageBytes, CancellationToken cancellationToken, IProgress<double> progress, string systemPromptOverride = null, string providerIdOverride = null)
{
var pasteConfig = userSettings?.PasteAIConfiguration;
var providerConfig = BuildProviderConfig(pasteConfig);
var providerConfig = BuildProviderConfig(pasteConfig, providerIdOverride);
if (systemPromptOverride != null)
{
providerConfig.SystemPrompt = systemPromptOverride;
}
return await TransformAsync(prompt, inputText, imageBytes, providerConfig, cancellationToken, progress);
}
@@ -148,13 +153,26 @@ namespace AdvancedPaste.Services.CustomActions
return serviceType == AIServiceType.Unknown ? AIServiceType.OpenAI : serviceType;
}
private PasteAIConfig BuildProviderConfig(PasteAIConfiguration config)
private PasteAIConfig BuildProviderConfig(PasteAIConfiguration config, string providerIdOverride = null)
{
config ??= new PasteAIConfiguration();
var provider = config.ActiveProvider ?? config.Providers?.FirstOrDefault() ?? new PasteAIProviderDefinition();
PasteAIProviderDefinition provider;
if (!string.IsNullOrWhiteSpace(providerIdOverride))
{
provider = config.Providers?.FirstOrDefault(p => string.Equals(p.Id, providerIdOverride, StringComparison.OrdinalIgnoreCase))
?? config.ActiveProvider
?? config.Providers?.FirstOrDefault()
?? new PasteAIProviderDefinition();
}
else
{
provider = config.ActiveProvider ?? config.Providers?.FirstOrDefault() ?? new PasteAIProviderDefinition();
}
var serviceType = NormalizeServiceType(provider.ServiceTypeKind);
var systemPrompt = string.IsNullOrWhiteSpace(provider.SystemPrompt) ? DefaultSystemPrompt : provider.SystemPrompt;
var apiKey = AcquireApiKey(serviceType);
var apiKey = AcquireApiKey(serviceType, provider.Id);
var modelName = provider.ModelName;
var providerConfig = new PasteAIConfig
@@ -173,15 +191,14 @@ namespace AdvancedPaste.Services.CustomActions
return providerConfig;
}
private string AcquireApiKey(AIServiceType serviceType)
private string AcquireApiKey(AIServiceType serviceType, string providerId)
{
if (!RequiresApiKey(serviceType))
{
return string.Empty;
}
credentialsProvider.Refresh();
return credentialsProvider.GetKey() ?? string.Empty;
return credentialsProvider.GetKey(serviceType, providerId ?? string.Empty);
}
private static bool RequiresApiKey(AIServiceType serviceType)
@@ -190,6 +207,8 @@ namespace AdvancedPaste.Services.CustomActions
{
AIServiceType.Onnx => false,
AIServiceType.Ollama => false,
AIServiceType.FoundryLocal => false,
AIServiceType.PhiSilica => false,
_ => true,
};
}

View File

@@ -12,6 +12,6 @@ namespace AdvancedPaste.Services.CustomActions
{
public interface ICustomActionTransformService
{
Task<CustomActionTransformResult> TransformAsync(string prompt, string inputText, byte[] imageBytes, CancellationToken cancellationToken, IProgress<double> progress);
Task<CustomActionTransformResult> TransformAsync(string prompt, string inputText, byte[] imageBytes, CancellationToken cancellationToken, IProgress<double> progress, string systemPromptOverride = null, string providerIdOverride = null);
}
}

View File

@@ -15,6 +15,7 @@ namespace AdvancedPaste.Services.CustomActions
SemanticKernelPasteProvider.Registration,
LocalModelPasteProvider.Registration,
FoundryLocalPasteProvider.Registration,
PhiSilicaPasteProvider.Registration,
};
private static readonly IReadOnlyDictionary<AIServiceType, Func<PasteAIConfig, IPasteAIProvider>> ProviderFactories = CreateProviderFactories();

View File

@@ -0,0 +1,208 @@
// 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;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using AdvancedPaste.Models;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.Windows.AI;
using Microsoft.Windows.AI.ContentSafety;
using Microsoft.Windows.AI.Text;
using PhiSilicaLanguageModel = Microsoft.Windows.AI.Text.LanguageModel;
namespace AdvancedPaste.Services.CustomActions;
public sealed class PhiSilicaPasteProvider : IPasteAIProvider
{
private static readonly IReadOnlyCollection<AIServiceType> SupportedTypes = new[]
{
AIServiceType.PhiSilica,
};
public static PasteAIProviderRegistration Registration { get; } = new(SupportedTypes, config => new PhiSilicaPasteProvider(config));
private static readonly SemaphoreSlim _initLock = new(1, 1);
private static PhiSilicaLanguageModel _cachedModel;
private readonly PasteAIConfig _config;
public PhiSilicaPasteProvider(PasteAIConfig config)
{
ArgumentNullException.ThrowIfNull(config);
_config = config;
}
public Task<bool> IsAvailableAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
PhiSilicaLafHelper.TryUnlock();
var readyState = PhiSilicaLanguageModel.GetReadyState();
return Task.FromResult(readyState is not (AIFeatureReadyState.NotSupportedOnCurrentSystem or AIFeatureReadyState.DisabledByUser));
}
catch (Exception)
{
return Task.FromResult(false);
}
}
public async Task<string> ProcessPasteAsync(PasteAIRequest request, CancellationToken cancellationToken, IProgress<double> progress)
{
ArgumentNullException.ThrowIfNull(request);
try
{
var systemPrompt = request.SystemPrompt;
if (string.IsNullOrWhiteSpace(systemPrompt))
{
throw new PasteActionException(
"System prompt is required for Phi Silica",
new ArgumentException("System prompt must be provided", nameof(request)));
}
var prompt = request.Prompt;
var inputText = request.InputText;
if (string.IsNullOrWhiteSpace(prompt) || string.IsNullOrWhiteSpace(inputText))
{
throw new PasteActionException(
"Prompt and input text are required",
new ArgumentException("Prompt and input text must be provided", nameof(request)));
}
cancellationToken.ThrowIfCancellationRequested();
var languageModel = await GetOrCreateModelAsync(cancellationToken).ConfigureAwait(false);
progress?.Report(0.1);
var contentFilterOptions = new ContentFilterOptions();
var context = languageModel.CreateContext(systemPrompt, contentFilterOptions);
var userPrompt = $"""
User instructions:
{prompt}
Text:
{inputText}
Output:
""";
if ((ulong)userPrompt.Length > languageModel.GetUsablePromptLength(context, userPrompt))
{
throw new PasteActionException(
"Prompt is too large for the Phi Silica model context",
new InvalidOperationException("Prompt exceeds usable prompt length"),
aiServiceMessage: "The input text is too large for on-device processing. Try with shorter text.");
}
var options = new LanguageModelOptions
{
ContentFilterOptions = contentFilterOptions,
};
var result = await languageModel.GenerateResponseAsync(context, userPrompt, options).AsTask(cancellationToken).ConfigureAwait(false);
progress?.Report(0.8);
if (result.Status != LanguageModelResponseStatus.Complete)
{
var statusMessage = result.Status switch
{
LanguageModelResponseStatus.BlockedByPolicy => "Response was blocked by policy.",
LanguageModelResponseStatus.PromptBlockedByContentModeration => "Prompt was blocked by content moderation.",
LanguageModelResponseStatus.ResponseBlockedByContentModeration => "Response was blocked by content moderation.",
LanguageModelResponseStatus.PromptLargerThanContext => "Prompt is too large for the model context.",
_ => $"Unexpected status: {result.Status}",
};
throw new PasteActionException(
$"Phi Silica returned status: {result.Status}",
new InvalidOperationException($"LanguageModel response status: {result.Status}"),
aiServiceMessage: statusMessage);
}
var responseText = result.Text ?? string.Empty;
request.Usage = AIServiceUsage.None;
progress?.Report(1.0);
return responseText;
}
catch (OperationCanceledException)
{
throw;
}
catch (PasteActionException)
{
throw;
}
catch (Exception ex)
{
throw new PasteActionException(
"Failed to generate response using Phi Silica",
ex,
aiServiceMessage: $"Error details: {ex.Message}");
}
}
private static async Task<PhiSilicaLanguageModel> GetOrCreateModelAsync(CancellationToken cancellationToken)
{
if (_cachedModel is not null)
{
return _cachedModel;
}
await _initLock.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
if (_cachedModel is not null)
{
return _cachedModel;
}
PhiSilicaLafHelper.TryUnlock();
var readyState = PhiSilicaLanguageModel.GetReadyState();
if (readyState is AIFeatureReadyState.NotSupportedOnCurrentSystem or AIFeatureReadyState.DisabledByUser)
{
throw new PasteActionException(
"Phi Silica is not supported on this device. A Copilot+ PC is required.",
new InvalidOperationException("Phi Silica requires a Copilot+ PC with an NPU."),
aiServiceMessage: "Phi Silica requires a Copilot+ PC with an NPU. For on-device AI on any Windows PC, consider using Foundry Local.");
}
if (readyState is AIFeatureReadyState.NotReady)
{
var ensureResult = await PhiSilicaLanguageModel.EnsureReadyAsync().AsTask(cancellationToken).ConfigureAwait(false);
if (ensureResult.Status != AIFeatureReadyResultState.Success)
{
throw new PasteActionException(
"Failed to prepare Phi Silica model",
ensureResult.ExtendedError,
aiServiceMessage: $"Model preparation failed (status: {ensureResult.Status})");
}
}
if (PhiSilicaLanguageModel.GetReadyState() is not AIFeatureReadyState.Ready)
{
throw new PasteActionException(
"Phi Silica model is not ready",
new InvalidOperationException("Phi Silica model is not in Ready state after preparation."),
aiServiceMessage: "Phi Silica model is not available. Please ensure the model is downloaded and ready.");
}
_cachedModel = await PhiSilicaLanguageModel.CreateAsync().AsTask(cancellationToken).ConfigureAwait(false);
return _cachedModel;
}
finally
{
_initLock.Release();
}
}
}

View File

@@ -175,6 +175,7 @@ namespace AdvancedPaste.Services.CustomActions
AIServiceType.OpenAI or AIServiceType.AzureOpenAI => new OpenAIPromptExecutionSettings
{
FunctionChoiceBehavior = null,
ReasoningEffort = "minimal",
},
_ => new PromptExecutionSettings(),
};

View File

@@ -55,6 +55,13 @@ public sealed class EnhancedVaultCredentialsProvider : IAICredentialsProvider
return !string.IsNullOrEmpty(GetKey());
}
public string GetKey(AIServiceType serviceType, string providerId)
{
var normalizedType = NormalizeServiceType(serviceType);
var entry = BuildCredentialEntry(normalizedType, providerId ?? string.Empty);
return LoadKey(entry);
}
public bool Refresh()
{
using (_syncRoot.EnterScope())
@@ -121,6 +128,7 @@ public sealed class EnhancedVaultCredentialsProvider : IAICredentialsProvider
try
{
var credential = new PasswordVault().Retrieve(entry.Value.Resource, entry.Value.Username);
credential?.RetrievePassword();
return credential?.Password ?? string.Empty;
}
catch (Exception)
@@ -160,6 +168,7 @@ public sealed class EnhancedVaultCredentialsProvider : IAICredentialsProvider
case AIServiceType.ML:
case AIServiceType.Onnx:
case AIServiceType.Ollama:
case AIServiceType.PhiSilica:
return null;
default:
return null;

View File

@@ -2,6 +2,8 @@
// 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.PowerToys.Settings.UI.Library;
namespace AdvancedPaste.Services;
/// <summary>
@@ -21,6 +23,14 @@ public interface IAICredentialsProvider
/// <returns>Credential string or <see cref="string.Empty"/> when missing.</returns>
string GetKey();
/// <summary>
/// Retrieves the credential for a specific AI provider.
/// </summary>
/// <param name="serviceType">The AI service type.</param>
/// <param name="providerId">The provider identifier.</param>
/// <returns>Credential string or <see cref="string.Empty"/> when missing.</returns>
string GetKey(AIServiceType serviceType, string providerId);
/// <summary>
/// Refreshes the cached credential for the active AI provider.
/// </summary>

View File

@@ -12,5 +12,5 @@ namespace AdvancedPaste.Services;
public interface IKernelService
{
Task<DataPackage> TransformClipboardAsync(string prompt, DataPackageView clipboardData, bool isSavedQuery, CancellationToken cancellationToken, IProgress<double> progress);
Task<DataPackage> TransformClipboardAsync(string prompt, DataPackageView clipboardData, bool isSavedQuery, CancellationToken cancellationToken, IProgress<double> progress, string providerIdOverride = null);
}

View File

@@ -46,7 +46,7 @@ public abstract class KernelServiceBase(
protected abstract IKernelRuntimeConfiguration GetRuntimeConfiguration();
public async Task<DataPackage> TransformClipboardAsync(string prompt, DataPackageView clipboardData, bool isSavedQuery, CancellationToken cancellationToken, IProgress<double> progress)
public async Task<DataPackage> TransformClipboardAsync(string prompt, DataPackageView clipboardData, bool isSavedQuery, CancellationToken cancellationToken, IProgress<double> progress, string providerIdOverride = null)
{
Logger.LogTrace();

View File

@@ -9,15 +9,18 @@ using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using AdvancedPaste.Services.CustomActions;
using AdvancedPaste.Settings;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Telemetry;
using Windows.ApplicationModel.DataTransfer;
namespace AdvancedPaste.Services;
public sealed class PasteFormatExecutor(IKernelService kernelService, ICustomActionTransformService customActionTransformService) : IPasteFormatExecutor
public sealed class PasteFormatExecutor(IKernelService kernelService, ICustomActionTransformService customActionTransformService, IUserSettings userSettings) : IPasteFormatExecutor
{
private readonly IKernelService _kernelService = kernelService;
private readonly ICustomActionTransformService _customActionTransformService = customActionTransformService;
private readonly IUserSettings _userSettings = userSettings;
public async Task<DataPackage> ExecutePasteFormatAsync(PasteFormat pasteFormat, PasteActionSource source, CancellationToken cancellationToken, IProgress<double> progress)
{
@@ -36,8 +39,9 @@ public sealed class PasteFormatExecutor(IKernelService kernelService, ICustomAct
return await Task.Run(async () =>
pasteFormat.Format switch
{
PasteFormats.KernelQuery => await _kernelService.TransformClipboardAsync(pasteFormat.Prompt, clipboardData, pasteFormat.IsSavedQuery, cancellationToken, progress),
PasteFormats.CustomTextTransformation => DataPackageHelpers.CreateFromText((await _customActionTransformService.TransformAsync(pasteFormat.Prompt, await clipboardData.GetTextOrHtmlTextAsync(), await clipboardData.GetImageAsPngBytesAsync(), cancellationToken, progress))?.Content ?? string.Empty),
PasteFormats.KernelQuery => await _kernelService.TransformClipboardAsync(pasteFormat.Prompt, clipboardData, pasteFormat.IsSavedQuery, cancellationToken, progress, pasteFormat.ProviderId),
PasteFormats.CustomTextTransformation => DataPackageHelpers.CreateFromText((await _customActionTransformService.TransformAsync(pasteFormat.Prompt, await clipboardData.GetTextOrHtmlTextAsync(), await clipboardData.GetImageAsPngBytesAsync(), cancellationToken, progress, providerIdOverride: pasteFormat.ProviderId))?.Content ?? string.Empty),
PasteFormats.FixSpellingAndGrammar => DataPackageHelpers.CreateFromText((await _customActionTransformService.TransformAsync(GetFixSpellingPrompt(), await clipboardData.GetTextOrHtmlTextAsync(), null, cancellationToken, progress, GetFixSpellingSystemPrompt(), pasteFormat.ProviderId))?.Content ?? string.Empty),
_ => await TransformHelpers.TransformAsync(format, clipboardData, cancellationToken, progress),
});
}
@@ -62,4 +66,16 @@ public sealed class PasteFormatExecutor(IKernelService kernelService, ICustomAct
throw new ArgumentOutOfRangeException(nameof(format));
}
}
private string GetFixSpellingPrompt()
{
var customPrompt = _userSettings.FixSpellingAndGrammarPrompt;
return string.IsNullOrWhiteSpace(customPrompt) ? AdvancedPasteDefaultPrompts.FixSpellingAndGrammar : customPrompt;
}
private string GetFixSpellingSystemPrompt()
{
var customSystemPrompt = _userSettings.FixSpellingAndGrammarSystemPrompt;
return string.IsNullOrWhiteSpace(customSystemPrompt) ? AdvancedPasteDefaultPrompts.FixSpellingAndGrammarSystem : customSystemPrompt;
}
}

View File

@@ -232,6 +232,12 @@
<data name="PasteAsPlainText" xml:space="preserve">
<value>Paste as plain text</value>
</data>
<data name="FixSpellingAndGrammar" xml:space="preserve">
<value>Fix spelling and grammar</value>
</data>
<data name="CoachingExplanationTitle.Text" xml:space="preserve">
<value>What was changed and why</value>
</data>
<data name="ImageToText" xml:space="preserve">
<value>Image to text</value>
</data>

View File

@@ -16,8 +16,8 @@ using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using AdvancedPaste.Services;
using AdvancedPaste.Services.CustomActions;
using AdvancedPaste.Settings;
using Common.UI;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using ManagedCommon;
@@ -41,6 +41,7 @@ namespace AdvancedPaste.ViewModels
private readonly IUserSettings _userSettings;
private readonly IPasteFormatExecutor _pasteFormatExecutor;
private readonly IAICredentialsProvider _credentialsProvider;
private readonly ICustomActionTransformService _customActionTransformService;
private CancellationTokenSource _pasteActionCancellationTokenSource;
@@ -258,11 +259,12 @@ namespace AdvancedPaste.ViewModels
public event EventHandler PreviewRequested;
public OptionsViewModel(IFileSystem fileSystem, IAICredentialsProvider credentialsProvider, IUserSettings userSettings, IPasteFormatExecutor pasteFormatExecutor)
public OptionsViewModel(IFileSystem fileSystem, IAICredentialsProvider credentialsProvider, IUserSettings userSettings, IPasteFormatExecutor pasteFormatExecutor, ICustomActionTransformService customActionTransformService)
{
_credentialsProvider = credentialsProvider;
_userSettings = userSettings;
_pasteFormatExecutor = pasteFormatExecutor;
_customActionTransformService = customActionTransformService;
GeneratedResponses = [];
GeneratedResponses.CollectionChanged += (s, e) =>
@@ -341,11 +343,21 @@ namespace AdvancedPaste.ViewModels
});
}
private PasteFormat CreateStandardPasteFormat(PasteFormats format) =>
PasteFormat.CreateStandardFormat(format, AvailableClipboardFormats, IsCustomAIServiceEnabled, ResourceLoaderInstance.ResourceLoader.GetString);
private PasteFormat CreateStandardPasteFormat(PasteFormats format)
{
var providerId = GetProviderIdForFormat(format);
return PasteFormat.CreateStandardFormat(format, AvailableClipboardFormats, IsCustomAIServiceEnabled, ResourceLoaderInstance.ResourceLoader.GetString, providerId);
}
private PasteFormat CreateCustomAIPasteFormat(string name, string prompt, bool isSavedQuery) =>
PasteFormat.CreateCustomAIFormat(CustomAIFormat, name, prompt, isSavedQuery, AvailableClipboardFormats, IsCustomAIServiceEnabled);
private PasteFormat CreateCustomAIPasteFormat(string name, string prompt, bool isSavedQuery, string providerId = null) =>
PasteFormat.CreateCustomAIFormat(CustomAIFormat, name, prompt, isSavedQuery, AvailableClipboardFormats, IsCustomAIServiceEnabled, providerId);
private string GetProviderIdForFormat(PasteFormats format) =>
format switch
{
PasteFormats.FixSpellingAndGrammar => _userSettings.FixSpellingAndGrammarProviderId,
_ => string.Empty,
};
private void UpdateAIProviderActiveFlags()
{
@@ -418,7 +430,7 @@ namespace AdvancedPaste.ViewModels
UpdateFormats(
CustomActionPasteFormats,
IsCustomAIServiceEnabled ? _userSettings.CustomActions.Select(customAction => CreateCustomAIPasteFormat(customAction.Name, customAction.Prompt, isSavedQuery: true)) : []);
IsCustomAIServiceEnabled ? _userSettings.CustomActions.Select(customAction => CreateCustomAIPasteFormat(customAction.Name, customAction.Prompt, isSavedQuery: true, customAction.ProviderId)) : []);
}
public void Dispose()
@@ -539,6 +551,7 @@ namespace AdvancedPaste.ViewModels
{
PasteActionError = PasteActionError.None;
Query = string.Empty;
CoachingExplanation = null;
await ReadClipboardAsync();
@@ -616,6 +629,12 @@ namespace AdvancedPaste.ViewModels
[ObservableProperty]
private string _customFormatResult;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HasCoachingExplanation))]
private string _coachingExplanation;
public bool HasCoachingExplanation => !string.IsNullOrEmpty(CoachingExplanation);
[RelayCommand]
public async Task PasteCustomAsync()
{
@@ -661,17 +680,37 @@ namespace AdvancedPaste.ViewModels
[RelayCommand]
public void OpenSettings()
{
SettingsDeepLink.OpenSettings(SettingsDeepLink.SettingsWindow.AdvancedPaste);
try
{
var exePath = System.IO.Path.Combine(
ManagedCommon.PowerToysPathResolver.GetPowerToysInstallPath(),
"PowerToys.exe");
if (exePath != null && System.IO.File.Exists(exePath))
{
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
{
FileName = exePath,
Arguments = "--open-settings=AdvancedPaste",
UseShellExecute = false,
});
}
}
catch (Exception ex)
{
Logger.LogError("Failed to open settings", ex);
}
GetMainWindow()?.Close();
}
internal async Task ExecutePasteFormatAsync(PasteFormats format, PasteActionSource source)
internal async Task ExecutePasteFormatAsync(PasteFormats format, PasteActionSource source, bool forceCoaching = false)
{
await ReadClipboardAsync();
await ExecutePasteFormatAsync(CreateStandardPasteFormat(format), source);
await ExecutePasteFormatAsync(CreateStandardPasteFormat(format), source, forceCoaching);
}
internal async Task ExecutePasteFormatAsync(PasteFormat pasteFormat, PasteActionSource source)
internal async Task ExecutePasteFormatAsync(PasteFormat pasteFormat, PasteActionSource source, bool forceCoaching = false)
{
if (IsBusy)
{
@@ -704,12 +743,30 @@ namespace AdvancedPaste.ViewModels
await delayTask;
var outputText = await dataPackage.GetView().GetTextOrEmptyAsync();
bool isCoachingAction = pasteFormat.Format == PasteFormats.FixSpellingAndGrammar &&
(forceCoaching || (_userSettings.FixSpellingAndGrammarCoachingEnabled && !_userSettings.FixSpellingAndGrammarCoachingShortcutSet));
bool shouldPreview = pasteFormat.Metadata.CanPreview && _userSettings.ShowCustomPreview && !string.IsNullOrEmpty(outputText) && source != PasteActionSource.GlobalKeyboardShortcut;
// Coaching mode forces preview even for global keyboard shortcuts
if (isCoachingAction && !string.IsNullOrEmpty(outputText))
{
shouldPreview = true;
}
if (shouldPreview)
{
GeneratedResponses.Add(outputText);
CurrentResponseIndex = GeneratedResponses.Count - 1;
if (isCoachingAction)
{
await GenerateCoachingExplanationAsync(outputText);
}
else
{
CoachingExplanation = null;
}
PreviewRequested?.Invoke(this, EventArgs.Empty);
}
else
@@ -730,6 +787,65 @@ namespace AdvancedPaste.ViewModels
Logger.LogDebug($"Finished executing {pasteFormat.Format} from source {source}; timeTakenMs={elapsedWatch.ElapsedMilliseconds}");
}
private async Task GenerateCoachingExplanationAsync(string correctedText)
{
try
{
var originalText = ClipboardData != null ? await ClipboardData.GetTextOrEmptyAsync() : string.Empty;
if (string.IsNullOrEmpty(originalText))
{
CoachingExplanation = null;
return;
}
static string NormalizeForComparison(string s) =>
s.Replace('\u2018', '\'') // left single quote
.Replace('\u2019', '\'') // right single quote / apostrophe
.Replace('\u201C', '"') // left double quote
.Replace('\u201D', '"') // right double quote
.Replace('\u2013', '-') // en dash
.Replace('\u2014', '-'); // em dash
if (string.Equals(NormalizeForComparison(originalText), NormalizeForComparison(correctedText), StringComparison.Ordinal))
{
CoachingExplanation = null;
return;
}
var coachingInstruction = string.IsNullOrWhiteSpace(_userSettings.FixSpellingAndGrammarCoachingPrompt)
? AdvancedPasteDefaultPrompts.FixSpellingAndGrammarCoaching
: _userSettings.FixSpellingAndGrammarCoachingPrompt;
var coachingInputText = $"Original:\n\"{originalText}\"\n\nCorrected:\n\"{correctedText}\"";
var coachingSystemPrompt = string.IsNullOrWhiteSpace(_userSettings.FixSpellingAndGrammarCoachingSystemPrompt)
? AdvancedPasteDefaultPrompts.FixSpellingAndGrammarCoachingSystem
: _userSettings.FixSpellingAndGrammarCoachingSystemPrompt;
var coachingProviderId = _userSettings.FixSpellingAndGrammarCoachingProviderId;
if (string.IsNullOrWhiteSpace(coachingProviderId))
{
coachingProviderId = _userSettings.FixSpellingAndGrammarProviderId;
}
var result = await _customActionTransformService.TransformAsync(
coachingInstruction,
coachingInputText,
null,
_pasteActionCancellationTokenSource?.Token ?? CancellationToken.None,
null,
coachingSystemPrompt,
string.IsNullOrWhiteSpace(coachingProviderId) ? null : coachingProviderId);
CoachingExplanation = result?.Content;
}
catch (Exception ex)
{
Logger.LogError("Error generating coaching explanation", ex);
CoachingExplanation = null;
}
}
internal async Task ExecutePasteFormatAsync(VirtualKey key)
{
var pasteFormat = StandardPasteFormats.Concat(CustomActionPasteFormats)

View File

@@ -1,40 +1,6 @@
#include <windows.h>
#include "resource.h"
#include "../../../../common/version/version.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"
#undef APSTUDIO_READONLY_SYMBOLS
1 VERSIONINFO
FILEVERSION FILE_VERSION
PRODUCTVERSION PRODUCT_VERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
BEGIN
VALUE "CompanyName", COMPANY_NAME
VALUE "FileDescription", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
END
END

View File

@@ -15,7 +15,6 @@
</PropertyGroup>
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">

View File

@@ -100,25 +100,30 @@ HRESULT AdvancedPasteProcessManager::start_process(const std::wstring& pipe_name
{
const unsigned long powertoys_pid = GetCurrentProcessId();
const auto executable_args = std::format(L"{} {}", std::to_wstring(powertoys_pid), pipe_name);
const auto launch_direct_exe = [&]() -> HRESULT {
// Fallback: launch exe directly (dev builds without GenerateAppxPackageOnBuild)
const auto executable_args = std::format(L"{} {}", std::to_wstring(powertoys_pid), pipe_name);
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
sei.lpFile = L"WinUI3Apps\\PowerToys.AdvancedPaste.exe";
sei.nShow = SW_SHOWNORMAL;
sei.lpParameters = executable_args.data();
if (ShellExecuteExW(&sei))
{
Logger::trace("Successfully started Advanced Paste process");
terminate_process();
m_hProcess = sei.hProcess;
return S_OK;
}
else
{
Logger::error(L"Advanced Paste process failed to start. {}", get_last_error_or_default(GetLastError()));
return E_FAIL;
}
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
sei.lpFile = L"WinUI3Apps\\PowerToys.AdvancedPaste.exe";
sei.nShow = SW_SHOWNORMAL;
sei.lpParameters = executable_args.data();
if (ShellExecuteExW(&sei))
{
Logger::trace("Successfully started Advanced Paste process (direct)");
terminate_process();
m_hProcess = sei.hProcess;
return S_OK;
}
else
{
Logger::error(L"Advanced Paste process failed to start. {}", get_last_error_or_default(GetLastError()));
return E_FAIL;
}
};
return launch_direct_exe();
}
HRESULT AdvancedPasteProcessManager::start_named_pipe_server(const std::wstring& pipe_name)
@@ -175,8 +180,9 @@ HRESULT AdvancedPasteProcessManager::start_named_pipe_server(const std::wstring&
}
}
// Wait for client.
const constexpr DWORD client_timeout_millis = 5000;
// Wait for client. AdvancedPaste under sparse identity can take >5s on cold start to
// bootstrap WinAppSDK + DI host before connecting back to this pipe.
const constexpr DWORD client_timeout_millis = 15000;
switch (WaitForSingleObject(overlapped.hEvent, client_timeout_millis))
{
case WAIT_OBJECT_0:

View File

@@ -66,6 +66,8 @@ namespace
const wchar_t JSON_KEY_PROVIDERS[] = L"providers";
const wchar_t JSON_KEY_SERVICE_TYPE[] = L"service-type";
const wchar_t JSON_KEY_ENABLE_ADVANCED_AI[] = L"enable-advanced-ai";
const wchar_t JSON_KEY_COACHING_SHORTCUT[] = L"coaching-shortcut";
const wchar_t JSON_KEY_COACHING_ENABLED[] = L"coaching-enabled";
const wchar_t JSON_KEY_VALUE[] = L"value";
}
@@ -255,6 +257,21 @@ private:
};
m_additional_actions.push_back(additionalAction);
// Register coaching shortcut as a separate hotkey with a "-coaching" suffix ID
if (action.HasKey(JSON_KEY_COACHING_SHORTCUT) && action.GetNamedBoolean(JSON_KEY_COACHING_ENABLED, false))
{
auto coachingHotkey = parse_single_hotkey(action.GetNamedObject(JSON_KEY_COACHING_SHORTCUT), actionIsShown);
if (coachingHotkey.key != 0)
{
const AdditionalAction coachingAction
{
std::wstring(actionName.c_str()) + L"-coaching",
coachingHotkey
};
m_additional_actions.push_back(coachingAction);
}
}
}
else
{
@@ -407,6 +424,7 @@ private:
// Define the expected order to ensure consistent hotkey ID assignment
const std::vector<winrt::hstring> expectedOrder = {
L"image-to-text",
L"fix-spelling-and-grammar",
L"paste-as-file",
L"transcode"
};
@@ -982,6 +1000,12 @@ public:
m_triggerEventWaiter.start(CommonSharedConstants::ADVANCED_PASTE_SHOW_UI_EVENT, [this](DWORD) {
// Same logic as hotkeyId == 1 (m_advanced_paste_ui_hotkey)
Logger::trace(L"AdvancedPaste ShowUI event triggered");
if (m_auto_copy_selection_custom_action)
{
send_copy_selection(); // best-effort; ignore failure
}
m_process_manager.start();
m_process_manager.bring_to_front();
m_process_manager.send_message(CommonSharedConstants::ADVANCED_PASTE_SHOW_UI_MESSAGE);
@@ -1032,13 +1056,11 @@ public:
}
}
if (is_custom_action_hotkey && m_auto_copy_selection_custom_action)
// Try to capture selected text for all hotkey actions when the setting is enabled.
// If nothing is selected (clipboard unchanged), fall through to use existing clipboard content.
if (m_auto_copy_selection_custom_action)
{
if (!send_copy_selection())
{
Logger::warn(L"Auto-copy: failed to copy selection for custom action index {} — aborting action", custom_action_index);
return false;
}
send_copy_selection(); // best-effort; ignore failure
}
m_process_manager.start();

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This file is read by XES, which we use in our Release builds. -->
<PropertyGroup Label="Version">
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
<XesBaseYearForStoreVersion>2025</XesBaseYearForStoreVersion>
<VersionMajor>0</VersionMajor>
<VersionMinor>9</VersionMinor>
<VersionInfoProductName>PowerToys Advanced Paste</VersionInfoProductName>
</PropertyGroup>
</Project>

View File

@@ -34,15 +34,17 @@ namespace winrt
using namespace Windows::Devices::Enumeration;
}
AudioSampleGenerator::AudioSampleGenerator(bool captureMicrophone, bool captureSystemAudio, bool micMonoMix)
AudioSampleGenerator::AudioSampleGenerator(bool captureMicrophone, bool captureSystemAudio, bool mixMicrophoneMono, bool useNoiseCancellation)
: m_captureMicrophone(captureMicrophone)
, m_captureSystemAudio(captureSystemAudio)
, m_micMonoMix(micMonoMix)
, m_mixMicrophoneMono(mixMicrophoneMono)
, m_useNoiseCancellation(useNoiseCancellation)
{
OutputDebugStringA(("AudioSampleGenerator created, captureMicrophone=" +
std::string(captureMicrophone ? "true" : "false") +
", captureSystemAudio=" + std::string(captureSystemAudio ? "true" : "false") +
", micMonoMix=" + std::string(micMonoMix ? "true" : "false") + "\n").c_str());
", mixMicrophoneMono=" + std::string(mixMicrophoneMono ? "true" : "false") +
", useNoiseCancellation=" + std::string(useNoiseCancellation ? "true" : "false") + "\n").c_str());
m_audioEvent.create(wil::EventOptions::ManualReset);
m_endEvent.create(wil::EventOptions::ManualReset);
m_startEvent.create(wil::EventOptions::ManualReset);
@@ -158,8 +160,24 @@ 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();
}
}
@@ -205,67 +223,65 @@ 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;
}
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;
// 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.
}
}
void AudioSampleGenerator::Start()
void AudioSampleGenerator::Start(int64_t videoStartTimestamp)
{
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();
@@ -284,7 +300,7 @@ void AudioSampleGenerator::Start()
m_loopbackCapture->Start();
}
m_audioGraph.Start();
// AudioGraph was already started in InitializeAsync for mic warmup.
}
}
@@ -611,12 +627,21 @@ void AudioSampleGenerator::CombineQueuedSamples()
void AudioSampleGenerator::OnAudioQuantumStarted(winrt::AudioGraph const& sender, winrt::IInspectable const& args)
{
// Don't process if we're not actively recording
// 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.
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();
@@ -628,6 +653,14 @@ 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;
@@ -636,7 +669,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_micMonoMix && m_captureMicrophone && numMicSamples > 0 && m_graphChannels >= 2)
if (m_mixMicrophoneMono && m_captureMicrophone && numMicSamples > 0 && m_graphChannels >= 2)
{
float* micData = reinterpret_cast<float*>(sampleBuffer.data());
uint32_t numFrames = numMicSamples / m_graphChannels;
@@ -656,6 +689,12 @@ 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)
@@ -733,13 +772,25 @@ void AudioSampleGenerator::OnAudioQuantumStarted(winrt::AudioGraph const& sender
if (sampleBuffer.Length() > 0)
{
auto sample = winrt::MediaStreamSample::CreateFromBuffer(sampleBuffer, timestamp.value());
// 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);
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 = timestamp.value();
m_lastSampleTimestamp = adjustedTs;
m_lastSampleDuration = winrt::TimeSpan{ durationTicks };
m_hasLastSampleTimestamp = true;
}

View File

@@ -1,18 +1,23 @@
#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 micMonoMix = false);
AudioSampleGenerator(bool captureMicrophone = true, bool captureSystemAudio = true, bool mixMicrophoneMono = false, bool useNoiseCancellation = false);
~AudioSampleGenerator();
winrt::Windows::Foundation::IAsyncAction InitializeAsync();
winrt::Windows::Media::MediaProperties::AudioEncodingProperties GetEncodingProperties();
std::optional<winrt::Windows::Media::Core::MediaStreamSample> TryGetNextSample();
void Start();
void Start(int64_t videoStartTimestamp = 0);
void Stop();
private:
@@ -70,5 +75,14 @@ private:
std::atomic<bool> m_started = false;
bool m_captureMicrophone = true;
bool m_captureSystemAudio = true;
bool m_micMonoMix = false;
};
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;
};

View File

@@ -0,0 +1,859 @@
//==============================================================================
//
// 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

@@ -0,0 +1,160 @@
//==============================================================================
//
// 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

@@ -0,0 +1,606 @@
#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

@@ -0,0 +1,115 @@
//==============================================================================
//
// 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,6 +77,7 @@ std::optional<CaptureFrame> CaptureFrameWait::TryGetNextFrame()
if (m_currentFrame != nullptr)
{
m_currentFrame.Close();
m_currentFrame = nullptr; // Prevent double-Close on subsequent calls
}
m_nextFrameEvent.ResetEvent();
@@ -107,6 +108,33 @@ 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)
@@ -121,6 +149,7 @@ 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,6 +108,7 @@ 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

@@ -0,0 +1,113 @@
#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

@@ -0,0 +1,37 @@
#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,6 +32,9 @@ 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;
@@ -106,6 +109,34 @@ 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];
@@ -123,8 +154,19 @@ 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
@@ -930,12 +972,20 @@ VideoRecordingSession::VideoRecordingSession(
winrt::GraphicsCaptureItem const& item,
RECT const cropRect,
uint32_t frameRate,
bool captureAudio,
bool captureSystemAudio,
bool micMonoMix,
std::unique_ptr<AudioSampleGenerator> audioGenerator,
winrt::IAsyncAction audioInitAction,
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());
@@ -1038,8 +1088,15 @@ 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;
}
});
@@ -1067,7 +1124,10 @@ VideoRecordingSession::VideoRecordingSession(
static_cast<WebcamCapture::Position>( g_WebcamPosition ),
static_cast<WebcamCapture::Size>( g_WebcamSize ),
webcamShape,
isFullScreenRecording );
isFullScreenRecording,
static_cast<WebcamBackgroundMode>( g_WebcamBackgroundMode ),
g_WebcamBackgroundImage,
static_cast<int>( g_WebcamBrightness ) );
m_webcamCapture->Start();
RecDiag( L"Constructor: WebcamCapture::Start() returned\n" );
}
@@ -1089,13 +1149,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 (captureAudio || captureSystemAudio)
{
// Always set up audio profile for loopback capture (stereo AAC)
auto audio = m_encodingProfile.Audio();
audio = winrt::AudioEncodingProperties::CreateAac(48000, 2, 192000);
m_encodingProfile.Audio(audio);
}
// 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(
@@ -1113,15 +1170,6 @@ 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
@@ -1155,6 +1203,7 @@ VideoRecordingSession::VideoRecordingSession(
VideoRecordingSession::~VideoRecordingSession()
{
Close();
RecDiagCloseFile();
}
@@ -1173,10 +1222,24 @@ winrt::IAsyncAction VideoRecordingSession::StartAsync()
// Create our MediaStreamSource
if(m_audioGenerator) {
RecDiag( L"StartAsync: co_await InitializeAsync...\n" );
co_await m_audioGenerator->InitializeAsync();
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: 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 {
@@ -1236,6 +1299,9 @@ 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 )
{
@@ -1285,28 +1351,45 @@ void VideoRecordingSession::OnMediaStreamSourceStarting(
winrt::MediaStreamSource const&,
winrt::MediaStreamSourceStartingEventArgs const& args)
{
RecDiag( L"OnStarting: entry, calling TryGetNextFrame...\n" );
auto frame = m_frameWait->TryGetNextFrame();
// 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;
if (frame) {
RecDiag( L"OnStarting: got frame, SystemRelativeTime=%lld (%.1fms)\n",
frame->SystemRelativeTime.count(),
frame->SystemRelativeTime.count() / 10000.0 );
args.Request().SetActualStartPosition(frame->SystemRelativeTime);
startSRT = frame->SystemRelativeTime.count();
RecDiag( L"OnStarting: got fresh frame, SRT=%lld (%.1fms)\n",
startSRT, startSRT / 10000.0 );
// 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.
args.Request().SetActualStartPosition( frame->SystemRelativeTime );
m_cachedStartingFrame = frame;
if (m_audioGenerator) {
RecDiag( L"OnStarting: calling AudioSampleGenerator::Start()\n" );
m_audioGenerator->Start();
RecDiag( L"OnStarting: audio started\n" );
}
m_adjustedStartSRT = startSRT;
} else {
RecDiag( L"OnStarting: TryGetNextFrame returned nullopt!\n" );
// 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" );
}
RecDiag( L"OnStarting: exit\n" );
}
@@ -1321,12 +1404,11 @@ std::shared_ptr<VideoRecordingSession> VideoRecordingSession::Create(
winrt::GraphicsCaptureItem const& item,
RECT const& crop,
uint32_t frameRate,
bool captureAudio,
bool captureSystemAudio,
bool micMonoMix,
std::unique_ptr<AudioSampleGenerator> audioGenerator,
winrt::IAsyncAction audioInitAction,
winrt::Streams::IRandomAccessStream const& stream)
{
return std::shared_ptr<VideoRecordingSession>(new VideoRecordingSession(device, item, crop, frameRate, captureAudio, captureSystemAudio, micMonoMix, stream));
return std::shared_ptr<VideoRecordingSession>(new VideoRecordingSession(device, item, crop, frameRate, std::move(audioGenerator), audioInitAction, stream));
}
//----------------------------------------------------------------------------
@@ -1338,10 +1420,6 @@ 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>())
@@ -1412,7 +1490,16 @@ void VideoRecordingSession::OnMediaStreamSourceSampleRequested(
else
{
// New desktop frame — crop and copy to back buffer.
timeStamp = frame->SystemRelativeTime;
// 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;
auto contentSize = frame->ContentSize;
auto frameTexture = GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame->FrameTexture);
D3D11_TEXTURE2D_DESC desc = {};
@@ -1448,7 +1535,7 @@ void VideoRecordingSession::OnMediaStreamSourceSampleRequested(
m_d3dContext->CopyResource( m_cachedDesktopTex.get(), backBuffer.get() );
}
// Log first 10 video frames with timing.
// Log first 50 video frames with timing.
if( !m_hasVideoSample.load() )
{
s_diagVideoCount = 0;
@@ -1462,13 +1549,14 @@ void VideoRecordingSession::OnMediaStreamSourceSampleRequested(
m_hasQpcOrigin = true;
}
s_diagVideoCount++;
if( s_diagVideoCount <= 10 )
if( s_diagVideoCount <= 50 )
{
RecDiag( L"SampleReq VIDEO #%d: sysRelTime=%lld deltaFromStart=%.1fms repeat=%d\n",
RecDiag( L"SampleReq VIDEO #%d: sysRelTime=%lld deltaFromStart=%.1fms repeat=%d cached=%d\n",
s_diagVideoCount,
timeStamp.count(),
( timeStamp.count() - s_diagStartTs ) / 10000.0,
isRepeatFrame ? 1 : 0 );
isRepeatFrame ? 1 : 0,
( s_diagVideoCount == 1 && cachedFrame.has_value() ) ? 1 : 0 );
}
#if _DEBUG
@@ -1492,6 +1580,11 @@ 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() );
}
@@ -1541,7 +1634,9 @@ void VideoRecordingSession::OnMediaStreamSourceSampleRequested(
}
catch (winrt::hresult_error const& error)
{
OutputDebugStringW(error.message().c_str());
RecDiag( L"SampleReq VIDEO EXCEPTION on frame #%d: hr=0x%08X %s\n",
s_diagVideoCount, static_cast<unsigned>(error.code()),
error.message().c_str() );
request.Sample(nullptr);
CloseInternal();
return;
@@ -1551,26 +1646,53 @@ 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 <= 10 )
if( s_diagAudioCount <= 50 )
{
RecDiag( L"SampleReq AUDIO #%d: timestamp=%lld (%.1fms)\n",
s_diagAudioCount,
sample.value().Timestamp().count(),
sample.value().Timestamp().count() / 10000.0 );
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 );
}
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)
{
OutputDebugStringW(error.message().c_str());
RecDiag( L"SampleReq AUDIO EXCEPTION on sample #%d: hr=0x%08X %s\n",
s_diagAudioCount, static_cast<unsigned>(error.code()),
error.message().c_str() );
request.Sample(nullptr);
CloseInternal();
return;
@@ -1659,7 +1781,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; }
@@ -2838,7 +2960,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);
@@ -2854,7 +2976,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);
@@ -5517,7 +5639,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,9 +27,8 @@ public:
winrt::GraphicsCaptureItem const& item,
RECT const& cropRect,
uint32_t frameRate,
bool captureAudio,
bool captureSystemAudio,
bool micMonoMix,
std::unique_ptr<AudioSampleGenerator> audioGenerator,
winrt::Windows::Foundation::IAsyncAction audioInitAction,
winrt::Streams::IRandomAccessStream const& stream);
~VideoRecordingSession();
@@ -217,9 +216,8 @@ private:
winrt::Capture::GraphicsCaptureItem const& item,
RECT const cropRect,
uint32_t frameRate,
bool captureAudio,
bool captureSystemAudio,
bool micMonoMix,
std::unique_ptr<AudioSampleGenerator> audioGenerator,
winrt::Windows::Foundation::IAsyncAction audioInitAction,
winrt::Streams::IRandomAccessStream const& stream);
void CloseInternal();
@@ -279,5 +277,9 @@ 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,11 +18,37 @@
#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:
@@ -44,7 +70,10 @@ public:
Position position,
Size size,
Shape shape,
bool fullScreenRecording = false );
bool fullScreenRecording = false,
WebcamBackgroundMode backgroundMode = WebcamBackgroundMode::None,
const wchar_t* backgroundImagePath = nullptr,
int brightness = 50 );
~WebcamCapture();
// Start/stop the capture thread.
@@ -106,6 +135,19 @@ 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;
@@ -135,6 +177,7 @@ 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;
@@ -142,6 +185,11 @@ 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;
@@ -163,8 +211,57 @@ 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

@@ -0,0 +1,143 @@
//==============================================================================
//
// 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

@@ -0,0 +1,616 @@
#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

@@ -0,0 +1,162 @@
#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.0",IDC_VERSION,42,7,73,10
LTEXT "ZoomIt v12.1",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,33 +267,52 @@ 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,61,71,80,12
LTEXT "Record Toggle:",IDC_STATIC,7,73,54,8
CONTROL "",IDC_RECORD_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,65,59,80,12
RTEXT "Record Toggle:",IDC_STATIC,7,62,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
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
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
CONTROL "Show &webcam overlay (Ctrl+C toggles)",IDC_WEBCAM_OVERLAY,
"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
"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
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;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;windowscodecs.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;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;windowscodecs.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;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;windowscodecs.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;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;windowscodecs.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;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;windowscodecs.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;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shlwapi.lib;comctl32.lib;odbc32.lib;odbccp32.lib;version.lib;Winmm.lib;gdiplus.lib;Msimg32.lib;Wtsapi32.lib;windowscodecs.lib;%(AdditionalDependencies)</AdditionalDependencies>
<UACUIAccess>true</UACUIAccess>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
@@ -257,6 +257,7 @@
<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>
@@ -266,6 +267,128 @@
<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>
@@ -314,6 +437,14 @@
<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>
@@ -338,16 +469,21 @@
<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,6 +13,12 @@
<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">
@@ -66,6 +72,48 @@
<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">
@@ -119,6 +167,27 @@
<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">
@@ -150,5 +219,6 @@
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natstepfilter" />
</ItemGroup>
</Project>

View File

@@ -56,12 +56,16 @@ 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};
@@ -116,11 +120,15 @@ 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,7 +21,6 @@
#include "PanoramaCapture.h"
#include <wtsapi32.h>
#include <tlhelp32.h>
#include <limits>
#include <vector>
#ifdef __ZOOMIT_POWERTOYS__
@@ -725,7 +724,7 @@ RunningOnWin64(
// this executable.
//
//--------------------------------------------------------------------
BOOLEAN ExtractImageResource( PTCHAR ResourceName, PTCHAR TargetFile )
BOOLEAN ExtractImageResource( PCTSTR ResourceName, PTCHAR TargetFile )
{
HRSRC hResource;
HGLOBAL hImageResource;
@@ -2835,6 +2834,323 @@ 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
@@ -2972,52 +3288,33 @@ 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 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);
}
// 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);
}
}
if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_WEBCAM_OVERLAY) {
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);
}
bool webcamEnabled = IsDlgButtonChecked(hDlg, IDC_WEBCAM_OVERLAY) == BST_CHECKED;
EnableWindow(GetDlgItem(hDlg, IDC_WEBCAM_SETTINGS), webcamEnabled);
}
// 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);
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);
}
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;
@@ -4577,7 +4874,6 @@ 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 = [&]()
{
@@ -4849,6 +5145,9 @@ 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 );
@@ -4924,101 +5223,19 @@ 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_MICROPHONE_LABEL), !isGifSelected);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_MICROPHONE), !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);
// Webcam overlay controls
CheckDlgButton( g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_OVERLAY,
g_WebcamOverlay ? BST_CHECKED : BST_UNCHECKED );
// Enumerate webcam devices
webcams.clear();
// Set initial enabled state for webcam overlay and settings button
{
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);
}
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_OVERLAY), !isGifSelected);
EnableWindow(GetDlgItem(g_OptionsTabs[RECORD_PAGE].hPage, IDC_WEBCAM_SETTINGS), !isGifSelected && g_WebcamOverlay);
}
if( GetFileAttributes( g_DemoTypeFile ) == -1 )
@@ -5372,6 +5589,7 @@ 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;
@@ -5385,15 +5603,8 @@ 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 settings
// Get the webcam overlay setting (other webcam settings are saved in WebcamSettingsProc)
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 )) {
@@ -6703,6 +6914,22 @@ 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 );
@@ -6791,9 +7018,8 @@ winrt::fire_and_forget StartRecordingAsync( HWND hWnd, LPRECT rcCrop, HWND hWndR
item,
*rcCrop,
g_RecordFrameRate,
g_CaptureAudio,
g_CaptureSystemAudio,
g_MicMonoMix,
std::move(audioGenerator),
audioInitAction,
stream );
_diagLog( L"VideoRecordingSession::Create returned" );

View File

@@ -25,4 +25,7 @@ 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,6 +127,16 @@
#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
@@ -142,7 +152,7 @@
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 120
#define _APS_NEXT_COMMAND_VALUE 40015
#define _APS_NEXT_CONTROL_VALUE 1124
#define _APS_NEXT_CONTROL_VALUE 1134
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@@ -0,0 +1,32 @@
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

@@ -0,0 +1,182 @@
/*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

@@ -0,0 +1,261 @@
/* 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

@@ -0,0 +1,174 @@
/* 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

@@ -0,0 +1,45 @@
/* 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

@@ -0,0 +1,56 @@
#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

@@ -0,0 +1,53 @@
/* 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

@@ -0,0 +1,505 @@
/* 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

@@ -0,0 +1,56 @@
/* 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

@@ -0,0 +1,601 @@
/*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

@@ -0,0 +1,203 @@
/*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

@@ -0,0 +1,123 @@
/* 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

@@ -0,0 +1,169 @@
/* 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

@@ -0,0 +1,257 @@
/* 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

@@ -0,0 +1,35 @@
/* 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

@@ -0,0 +1,159 @@
/* (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

@@ -0,0 +1,237 @@
/* 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

@@ -0,0 +1,528 @@
/* 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

@@ -0,0 +1,147 @@
/* 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

@@ -0,0 +1,60 @@
/* 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

@@ -0,0 +1,49 @@
/* 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

@@ -0,0 +1,131 @@
/* 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

@@ -0,0 +1,55 @@
#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

@@ -0,0 +1,874 @@
/* 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, };

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