USE AT YOUR OWN RISK
THIS IS WIP
IT MIGHT BURN YOUR HOUSE ;-)
GPU Normalize Audio is a Tdarr FlowPlugin that tries to move FFmpeg loudnorm-style audio normalization onto the GPU while matching Tdarr's normal CPU-only Normalize Audio output.
What it does today:
- Normalizes every audio stream in a file, not only the first one.
- Keeps video, subtitles, chapters, metadata, attachments, and data streams.
- Uses CUDA for the loudness stats/apply path and streams decode/encode through FFmpeg.
- Avoids huge raw PCM bridge files for normal
gpuSourcePortjobs. - Keeps decoded audio parity as the top priority, even when that costs speed.
- Release smoke testing covers multiple input codec/layout cases, including AAC stereo, MP3 stereo, AC3 5.1, E-AC-3 5.1, DTS 5.1, and multi-audio inputs.
What we are trying to do:
- Match CPU
Normalize Audiooutput first. - Make the GPU path faster over time without cheating parity.
- Keep improving long-media speed; the current release beats CPU on tested long combined jobs because the original 5.1 normalization is faster, while generated 2-channel fallback work is still slower by itself.
Latest release: v1.2.1.
1.2.1 keeps the streaming gpuSourcePort path, adds Normalize ONLY Languages, improves default job logs, removes stale hidden flow inputs, and preserves exact decoded parity against Tdarr CPU Normalize Audio. By default the release UI prefers English for both Track Order and Normalize ONLY Languages (eng,en). Clear Normalize ONLY Languages or enter all to normalize every audio stream. If the selected languages match no streams, the plugin passes the file through unchanged without taking the GPU lock or muxing.
The table below uses a 5.1 source that has no existing stereo track. The GPU job writes both the normalized original 5.1 stream and the generated normalized 2-channel fallback, then both decoded streams are compared against Tdarr CPU Normalize Audio output. SRC_CPU time is split the same way: the original CPU Normalize Audio job for the 5.1 stream, plus a CPU Normalize Audio reference for the generated 2-channel source.
Speed vs CPU uses measured SRC_CPU/GPU wall time: above 1.0x is faster than CPU, below 1.0x is slower than CPU.
60min TLDR: original 5.1 2.279x faster (1027.3s CPU vs 450.7s GPU), generated 2ch 0.715x slower (291.2s CPU vs 407.0s GPU), combined job 1.530x faster (1318.5s CPU vs 861.9s GPU), parity pass for both decoded streams.
| Workload | CPU reference | GPU 1.2.1 |
Speed vs CPU | Result |
|---|---|---|---|---|
| Original 5.1 | 1027.3s |
450.7s |
2.279x |
|
| Generated 2ch | 291.2s |
407.0s |
0.715x |
|
| Combined | 1318.5s |
861.9s |
1.530x |
| Workload | CPU reference | GPU 1.2.1 |
Speed vs CPU | Result |
|---|---|---|---|---|
| Original 5.1 | 540.3s |
229.9s |
2.350x |
|
| Generated 2ch | 155.5s |
206.7s |
0.753x |
|
| Combined | 695.8s |
438.5s |
1.587x |
| Workload | CPU reference | GPU 1.2.1 |
Speed vs CPU | Result |
|---|---|---|---|---|
| Original 5.1 | 168.6s |
78.4s |
2.149x |
|
| Generated 2ch | 49.9s |
71.8s |
0.696x |
|
| Combined | 218.5s |
150.9s |
1.448x |
| Workload | CPU reference | GPU 1.2.1 |
Speed vs CPU | Result |
|---|---|---|---|---|
| Original 5.1 | 18.3s |
9.6s |
1.912x |
|
| Generated 2ch | 5.0s |
11.0s |
0.452x |
|
| Combined | 23.3s |
20.8s |
1.123x |
| Workload | CPU reference | GPU 1.2.1 |
Speed vs CPU | Result |
|---|---|---|---|---|
| Original 5.1 | 9.8s |
6.6s |
1.496x |
|
| Generated 2ch | 2.6s |
6.4s |
0.405x |
|
| Combined | 12.4s |
13.1s |
0.948x |
Short clips are still slower because fixed startup, decode/encode, and stream setup overhead dominate. Longer media is the intended target and is where the GPU path now catches and passes CPU on the tested 5.1 plus generated-2ch workload.
Codec smoke coverage for 1.2.1 also passed exact decoded parity for AAC stereo, MP3 stereo, AC3 5.1, E-AC-3 5.1, DTS 5.1, and a multi-audio sample. The multi-audio smoke also verifies language-scope behavior: blank means all languages, omitted means release default English, no-match copies all audio unchanged, and German-only scopes select only German audio.
2-channel fallback behavior:
| Setting | Default | Behavior |
|---|---|---|
Add Generated 2-Channel Track |
true |
Adds normalized generated stereo track(s) according to the selected 2-channel scope. |
Track Order |
eng,en |
Comma-separated language priority used before choosing the generated 2-channel source. The default prefers English, while unlisted languages keep source order after listed languages. This uses source metadata and cannot fix missing or wrong language tags. |
Normalize ONLY Languages |
eng,en |
Comma-separated language filter for streams to normalize. Clear it or enter all to normalize every audio stream. If no selected language exists, all audio streams are copied unchanged. |
Only Generate 2-Channel For First Language |
true |
Ensures the first Track Order language has stereo. If that language lacks stereo, creates one generated track from the best same-language source, preferring 5.1/6-channel over 7.1/8-channel. Disable it to create one for every non-stereo audio stream/language. |
Compared with releases before 1.1.11, the default tested contract when Add Generated 2-Channel Track=true includes generated 2-channel fallback work, and parity is checked for both the original audio stream and the generated fallback. Older release rows below used the previous primary-stream speed table, so treat the timing comparison as release history rather than a strict same-workload benchmark.
Choose 1.2.1 for current parity, language-only normalization controls, readable default job logs, deploy cleanup, and corrected generated-stereo first-language behavior. Use 1.2.0 only if you need the previous no-language-filter UI.
Download the latest GitHub release zip and extract it into your Tdarr plugins folder.
Keep the version folder. Do not flatten it.
Correct layout:
FlowPlugins/CommunityFlowPlugins/audio/gpuNormalizeAudio/1.2.1/
Tdarr loads:
FlowPlugins/CommunityFlowPlugins/audio/gpuNormalizeAudio/1.2.1/index.js
Defaults:
Add Generated 2-Channel Track=trueTrack Order=eng,enNormalize ONLY Languages=eng,en(clear it or useallto normalize every language)Only Generate 2-Channel For First Language=trueMax Concurrent Jobs=1Audio Bitrate=192kIntegrated Loudness I=-18.0Loudness Range LRA=7.0True Peak TP=-2.0Max Gain dB=15Debug Logging=false
| Version | Use |
|---|---|
1.2.1 |
Current release. Adds language-only normalization, no-match pass-through, cleaner default logs, deploy cleanup, and matches CPU decoded output for normalized 5.1 plus generated 2-channel fallback on the required matrix. |
1.2.0 |
Previous release. Fixes generated-stereo first-language scope, keeps streaming gpuSourcePort, and matches CPU decoded output for normalized 5.1 plus generated 2-channel fallback on the required matrix. |
1.1.13 |
Previous release. Speeds up long generated-stereo fallback jobs again, keeps streaming gpuSourcePort, and matches CPU decoded output for normalized 5.1 plus generated 2-channel fallback on the required matrix. |
1.1.12 |
Older release. Speeds up long generated-stereo fallback jobs, keeps streaming gpuSourcePort, and matches CPU decoded output for normalized 5.1 plus generated 2-channel fallback on the required matrix. |
1.1.11 |
Keeps streaming gpuSourcePort, matches CPU decoded output for normalized 5.1 plus generated 2-channel fallback on tested long media, and adds language-priority controls for first-language versus all-language 2-channel fallback generation. |
1.1.10 |
Uses smaller streaming chunks, stats decode prefetch, and safe unsafe-output-feedback skipping after threshold lock; required parity matrix passed. Faster than CPU on all tested required limiter-heavy primary-stream cases. |
1.1.9 |
Parallelizes safe feedback-skip apply windows while preserving exact state and decoded parity; required parity matrix passed. Faster than CPU on tested 10min/30min limiter-heavy media. |
1.1.8 |
Skips safe output-feedback accumulation after the feedback threshold is active while preserving normal limiter/output rendering; required parity matrix passed. Still slower than CPU on long limiter-heavy media. |
1.1.7 |
Slot-accumulated safe feedback, parallel unsafe feedback, smaller chunks, and asymmetric risk planning speed up limiter-heavy 5.1 jobs; required parity matrix passed. Still slower than CPU on long limiter-heavy media. |
1.1.6 |
Segmented exact safe feedback speeds up limiter-heavy 5.1 jobs, required parity matrix passed. Still slower than CPU on long limiter-heavy media. |
1.1.5 |
Modularized GPU runtime, required parity matrix passed. Performance is effectively unchanged from 1.1.4; still slower than CPU on long limiter-heavy media. |
1.1.4 |
Faster exact stats and limiter-active GPU path, required parity matrix passed. Still slower than CPU on long limiter-heavy media. |
1.1.3 |
Faster limiter-active GPU path, required parity matrix passed. Still slower than CPU on long limiter-heavy media. |
1.1.2 |
Streaming two-pass release. Avoids huge raw PCM bridge files and fixed long-case parity. |
1.1.1 |
Added guarded GPU normalize concurrency. |
1.1 |
Older optimized exact GPU line. |
1.0 |
First stable CPU-output matching line. |
0.0.x |
Old pre-stable snapshots. Some are known not to match CPU normalizer output. Use only for rollback/debug. |