Skip to content

MSIVST/WMAEncodeRS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WMAEncodeRS

A command-line Windows Media Audio encoder for Windows — a Rust port of lvqcl's WMAEncode, extended to also emit xWMA (the RIFF format used by XAudio2 in games, à la Microsoft's xWMAEncode). It turns WAV / raw PCM into fully-working .wma or .xwma using the built-in Windows Media Format runtime (wmvcore.dll) — the same encoder Windows itself uses — so output is maximally compatible and DRM-free. It is bidirectional: a .wma input is decoded back to WAV.

WAV/PCM -> WMA via the built-in Windows Media Format SDK (wmvcore). Supports WMA Standard, Professional, Lossless, and Voice in CBR / VBR / two-pass modes, with tagging, raw-PCM input, stdin input, stdout piping, and .xwma output.

Highlights

  • Memory-safe at the boundary. All unsafe is confined to src/encoder.rs and src/xwma.rs (the COM/FFI boundary); the crate root sets #![deny(unsafe_code)], so every other module is compiler-guaranteed unsafe-free. (Calling the Windows Media SDK requires unsafe; it can only be isolated and audited, not removed.)
  • Bug fixes vs. the original C++: tag strings include the UTF-16 NUL terminator (WMT_TYPE_STRING length); 64-bit WAV offset/seek (two-pass on files > 2 GB); broken-pipe-safe stdout (--print-formats | head won't panic).
  • xWMA output with an accurate dpds seek table (total length exact; seek points sub-millisecond), validated by round-tripping through Microsoft's xwmaencode.exe.
  • Unicode-safe. Non-ASCII filenames and tags (incl. CJK) are handled without panic or mojibake; the console output code page is set to UTF-8 at startup.
  • Bidirectional. Encodes WAV→WMA/xWMA and decodes WMA→WAV and xWMA→WAV.

v0.5.1 CLI corrections

  • Adds vox as a shorthand for the Voice codec.
  • Rejects unsupported VBR qualities and option combinations instead of ending with the generic Requested encoder not found error or silently ignoring -q.
  • Clarifies quality VBR, Lossless profile availability, and reliable foobar2000 two-pass presets.

Platform and build

Supported operating systems are 64-bit Windows 10 and Windows 11. Windows XP, Vista, 7, and 8 are not supported. Building requires the Rust toolchain (x86_64-pc-windows-msvc) and the Windows Media runtime included with Windows. No external libraries are required; the windows crate binds wmvcore.dll directly.

cargo build --release
# -> target/release/WMAEncodeRS.exe

Prebuilt x86-64 binaries are published on the Releases page.

Usage

WMAEncodeRS <input> [output] [options]
  • <input> — WAV PCM file, or - for stdin
  • [output] — output file; .xwma extension (or --xwma) selects xWMA; - streams the result to stdout; omitted derives <input>.wma
WMAEncodeRS song.wav                      # -> song.wma, WMA Standard CBR 128k
WMAEncodeRS song.wav out.wma -q 75        # VBR quality 75
WMAEncodeRS song.wav out.wma -c pro -b 192
WMAEncodeRS song.wav out.wma -c lossless
WMAEncodeRS song.wav out.wma -m cbr2pass -b 96
WMAEncodeRS song.wav sfx.xwma -b 48       # xWMA for XAudio2 (48 kbps)
WMAEncodeRS song.wma song.wav             # decode WMA -> WAV (bidirectional)
WMAEncodeRS song.wav out.wma -v           # verbose: print format details

Decoding (WMA / xWMA → WAV)

A .wma or .xwma input is decoded to a PCM WAV (output .wav, or derived if omitted):

WMAEncodeRS music.wma music.wav
WMAEncodeRS sfx.xwma  sfx.wav

.wma decodes directly via the Windows Media reader. .xwma is decoded by reconstructing an ASF from its packets, then decoding that — output is ~99.9% identical to Microsoft's xwmaencode decoder (sub-LSB boundary differences). Also, decoded length may exceed the original — WMA adds encoder-delay padding. See more in Caveats below.

xWMA (XAudio2) output

.xwma output (or --xwma) produces a RIFF/XWMA file: WMA2 (Standard) CBR with a dpds seek table — the format XAudio2 plays directly. It is always WMA2/CBR; supported bitrates are 20, 32, 48, 64, 96, 160, 192 kbps (default 48). This reimplements both directions of Microsoft's xWMAEncode natively in Rust (no temp tools, no DirectX SDK): PCM→xWMA, and xWMA→WAV (see Decoding above).

Piping (Foobar2000 and friends)

WMA/ASF can't truly stream to a pipe (the muxer must seek back to patch header sizes), so when the output is - the result is encoded to a temp file and then streamed to stdout. WAV-on-stdin is fully supported — the model Foobar2000's converter uses:

cat song.wav | WMAEncodeRS -s - out.wma          # WAV in via stdin
cat song.wav | WMAEncodeRS -s - -  > out.wma     # WAV in, WMA out via stdout

Options

--quality <n>, -q <n>   one-pass VBR quality (10,25,50,75,90,98)
--bitrate <n>, -b <n>   bitrate for CBR/two-pass (default 128; xWMA default 48)
--codec <name>, -c <name>
                        standard|std (default), professional|pro,
                        lossless|lsl, voice|vox
--mode <name>, -m <name>
                        cbr (Standard/Pro default without -q), cbr2pass,
                        vbr, vbr2pass
--xwma                  emit xWMA (RIFF/XWMA for XAudio2); also auto-on for .xwma
--out-samplerate n      target sample rate
--out-channels n        target channel count
--out-bitdepth n        target bit depth
--ignorelength, -i      ignore the length in the WAV header
--bufferstdin           accepted for old command lines; has no effect
--raw                   headerless raw PCM input
--raw-samplerate / --raw-channels / --raw-bitdepth
--priority, --nice      run at idle priority
--verbose, -v           print format details to stderr
--silent, -s            suppress progress
--print-formats         show runtime DLL version and its codec/profile table
--help, -h              full help; --longhelp  extended help with examples
Tagging: --title --tracknumber --artist --album --year --genre
         --composer --albumartist --discnumber --comment  --tag name=value

-q implies one-pass vbr when --mode is omitted. Quality 100 does not select WMA Lossless; use --codec lossless without --quality. Both two-pass modes are bitrate-based and use -b. In quality-based VBR, the resulting bitrate varies with the audio; the 128 kbps default applies only to bitrate-based modes.

Runtime codec/profile table

--print-formats asks the loaded Windows Media runtime for its actual audio codecs and profiles through IWMCodecInfo3. It prints the loaded wmvcore.dll path and file version first, followed by a table grouped by the codec names that runtime exposes. It does not use a hard-coded profile list or scan third-party codec DLLs.

The table's mode, pass count, bitrate or quality, sample rate, channel count, bit depth, and channel mask come from the runtime's structured codec media types. A six-channel row is labeled 6 (5.1 ...) only when the returned Microsoft speaker mask confirms that layout; otherwise it is shown as 6 (layout unspecified). The Flags column retains runtime distinctions such as Low Delay and A/V optimized.

Example header (the path, version, codecs, and rows depend on the current Windows installation):

Windows Media runtime:
  DLL: %SystemRoot%\System32\wmvcore.dll
  File version: <installed file version>
  Source: codecs and profiles enumerated from this runtime via IWMCodecInfo3

Two-pass stdin and --bufferstdin

A seekable input file is read directly for each pass. Literal - stdin cannot be rewound, so a Standard/Professional two-pass encode always copies that input to a temporary disk file and replays it for the second pass.

--bufferstdin is retained only so older command lines are still accepted. It has no effect: it does not enable RAM buffering, does not force disk buffering, and is not required for two-pass encoding.

Notes on WMA behavior

Encoding goes through the Microsoft encoder, so output behaves exactly like Windows-produced WMA. A few inherent codec/container traits are by design and not controllable by any front-end:

  1. CBR leaky-bucket bandwidth limiting (use VBR/2-pass to avoid it)
  2. WMA Pro being undecodable on very old players (Standard is the default).
  3. Fixed-size ASF packet padding, and ASF's fixed header overhead on very short files.

This tool produces unprotected (DRM-free) files, handles the codec's bitrate↔sample-rate format matrix gracefully, and writes correct UTF-16 tags.

Low-delay WMA: what it is — and what it is not

A low-delay WMA profile uses a smaller stream buffer window than the equivalent normal profile. It is intended for streamed audio that must switch quickly, such as live broadcasts or a service that lets the listener change channels. The benefit is reduced stream-switching latency and live-broadcast delay.

Low delay does not make offline encoding faster, reduce Windows audio-device latency, or make the codec lossless. It is unrelated to --nice, --silent, and --ignorelength. A two-pass file can use a low-delay output profile, but it still requires two encoding passes; "low delay" describes the resulting stream's buffering behavior, not the time needed to encode it.

Microsoft exposes low-delay WMA Standard and Professional formats only in CBR mode, for either one or two passes. They are separate installed profiles whose descriptions contain Low Delay. Their bitrate is one kilobit below the equivalent normal profile: for example, 191 kbps selects the low-delay counterpart of the normal 192 kbps profile. Therefore, -b 192 selects the normal profile, while -b 191 selects low delay when that format is available.

Goal Command
Standard, one-pass low delay WMAEncodeRS.exe input.wav output.wma -c std -m cbr -b 191 -v
Professional, one-pass low delay WMAEncodeRS.exe input.wav output.wma -c pro -m cbr -b 191 -v
Standard, two-pass low delay WMAEncodeRS.exe input.wav output.wma -c std -m cbr2pass -b 191 -v
Professional, two-pass low delay WMAEncodeRS.exe input.wav output.wma -c pro -m cbr2pass -b 191 -v

Available bitrates depend on the Windows Media runtime and the requested sample rate, channel count, and bit depth. List the formats installed on the current computer and look for Low Delay under the desired codec and CBR pass mode:

WMAEncodeRS.exe --print-formats

Use the exact odd bitrate shown by that profile. If 191 kbps is not listed for the input format, choose another listed low-delay bitrate or adjust the output format. Quality VBR and two-pass VBR cannot be low delay, so do not combine -q, -m vbr, or -m vbr2pass with these profiles.

For foobar2000, use piped input for one pass and a seekable %s input for two passes:

Goal foobar2000 parameters
Standard, one-pass low delay - %d -s -c std -m cbr -b 191
Professional, one-pass low delay - %d -s -c pro -m cbr -b 191
Standard, two-pass low delay %s %d -s -c std -m cbr2pass -b 191
Professional, two-pass low delay %s %d -s -c pro -m cbr2pass -b 191

See Microsoft's documentation for Low-Delay Audio and Getting Low-Delay Audio Formats.

WMA Lossless profiles

WMA Lossless supports high-resolution and discrete multichannel audio, but the Windows runtime exposes specific profiles rather than every channel/rate/depth combination. A typical current runtime offers stereo and 5.1 profiles up to 24-bit/96 kHz, but no 4-channel Lossless profile. Use --print-formats to see the exact profiles installed on the current system.

--out-channels 2 can make a 4-channel input encodable by downmixing it to stereo. The stereo result is then compressed losslessly, but the downmix does not preserve the original discrete channel layout.

Caveats & technical notes

WMA vs. xWMA vs. XMA — they are not the same thing

Format Container Codec Typical use This tool
.wma ASF WMA Std/Pro/Lossless/Voice Windows desktop / media players / general audio ✅ encode + decode
.xwma RIFF WMA2 only XAudio2 game audio (Windows, Xbox One/Series, Xbox 360) ✅ encode + decode
.xma RIFF XMA (a different codec) Xbox 360 only (decoded by dedicated 360 hardware) ❌ not supported

The common trap: .xma is not WMA. XMA is a separate, Xbox-360-specific codec (a relative of WMA Pro) that has nothing to do with wmvcore or the WMA encoder. It can only be produced with xma2encode from the Xbox 360 XDK (a restricted SDK); there is no public Windows API/DLL that encodes XMA, so it is out of scope here. .wma is for general Windows audio; .xwma is for XAudio2 game audio.

ASF "Play Duration" rounding (foobar2000 length warning)

A player such as foobar2000 may warn that an encoded .wma's reported length is slightly shorter than the decoded length (e.g. 5:26.008000 vs 5:26.008163). The audio is complete — this is a metadata rounding artifact, not missing samples.

ASF stores durations as integers in 100-nanosecond units, and WMA decodes in fixed ~2048-sample frames, so the true length rarely lands on an exact tick. When wmvcore writes the ASF header it rounds the Play Duration down, losing the sub-tick remainder (here ~163 µs, about 7 samples at 44.1 kHz — inaudible). This is wmvcore's behavior, not the front-end's: the original wmaencode.exe (and Windows Media Player) produce the same warning, because we all hand PCM to the same IWMWriter muxer, which computes and writes that field. We don't get to override it through the high-level API.

Note: do not try to "fix" this by padding the file with silence — WMA can only add whole ~46 ms frames (vastly larger than the 0.163 ms discrepancy), and any appended silence becomes real audio that defeats WMA's gapless trim metadata, manufacturing an audible gap. The only safe fix would be to post-process the header's duration field (left undone here as a cosmetic, risky change).

xWMA → WAV decode: Why an ASF is reconstructed

The only WMA decoder available on a normal PC is the Windows Media runtime, reached through IWMSyncReader — which only opens ASF, not RIFF/XWMA. An .xwma is WMA2 audio (decodable) in a RIFF wrapper (not openable). So to decode it we must get the WMA2 packets into a container the reader accepts.

We can't feed raw packets to IWMSyncReader (no such entry point), and driving the WMA decoder DMO (WMADMOD.DLL) directly requires the WMA2 codec private data (samples- per-block, encode options, …) that the bare xWMA fmt chunk (cbSize = 0) deliberately omits. So instead we ask IWMCodecInfo3 for the WMA2 stream config matching the xWMA's rate/channels/bitrate — that config carries the missing codec private data — and write the (already-compressed) packets into a fresh ASF with IWMWriterAdvanced::WriteStreamSample. That ASF is then decoded by the normal .wma → WAV path. The reconstruction is the mechanism that supplies the codec metadata the container left out, and it reuses a single decoder instead of a second implementation.

xWMA decode is reference-accurate, not bit-exact

Because the reconstructed-ASF decode routes packets through IWMSyncReader rather than xWMA's native decoder, the decoded PCM is ~99.9 % byte-identical to Microsoft's xwmaencode (in testing, 162 of 182,318 bytes differed; ±1-LSB, ~−90 dB).

The differences sit at packet boundaries because WMA is a lapped-transform codec: consecutive frames overlap and cross-fade (overlap-add / inverse MDCT), so edge samples are reconstructed from two packets at once. Two things make the boundary samples round differently between the two decode paths: (1) decoder priming/preroll — our reconstructed stream's warm-up isn't bit-identical to the native xWMA decoder's; and (2) floating-point rounding order in the inverse transform, which tips the final quantize-to-int by ±1 on occasional edge samples. Long identical interiors, tiny ±1 differences at the seams — the expected fingerprint. Lossy decoders are only required to be correct within tolerance, not bit-exact across implementations (the same is true of decoding one MP3 with two decoders). It is inaudible and irrelevant to playback; it would only matter for byte-exact reproducibility (e.g. hashing decoded output), which would require driving WMADMOD.DLL directly instead.


Settings for adding WMAEncodeRS as a custom external encoder in foobar2000's Converter.

foobar2000 → Converter → Encoder presets → Add New

Encoder (full path to the binary): \path\to\your\WMAEncodeRS.exe

Extension — what the output file gets: wma (use xwma if you're targeting XAudio2 instead — see the variants below)

Parameters — without %s, foobar pipes WAV to stdin (-) and substitutes the output path for %d:

  • %d -s -b 192

For two-pass encoding, use %s instead. Foobar then creates a seekable temporary WAV, which WMAEncodeRS can read once per pass.

Format islossy (WMA Standard/Pro are lossy). Choose lossless only for the WMA-lossless preset below.

Highest BPS mode supported16 (WMA Standard/xWMA decode/encode at 16-bit).

What each token means

Token Meaning
- read WAV from stdin (foobar pipes it in)
%s foobar creates a temporary input WAV and substitutes its filename
%d foobar replaces this with the output file path
-s silent (suppress progress on stderr)
-b 192 bitrate 192 kbps (or -q 75 for VBR, etc.)

WMAEncodeRS accepts options before, between, or after the positional paths. The first positional argument is input and the second is output, so %s %d is the clearest file-mode form.

Ready-made presets

Goal Extension Parameters Format is
WMA Standard CBR 192k wma - %d -s -b 192 lossy
WMA Standard VBR q75 wma - %d -s -q 75 lossy
WMA Professional 192k wma - %d -s -c pro -b 192 lossy
WMA Lossless wma - %d -s -c lossless lossless
WMA Voice 20k (mono) wma - %d -s -c voice -b 20 lossy
xWMA 48k (XAudio2) xwma - %d -s -b 48 --xwma lossy
xWMA 192k (XAudio2) xwma - %d -s -b 192 --xwma lossy

Ready-made preset — WMA 2-pass VBR (ABR)

Goal Parameters
WMA Std 2-pass ABR 128k %s %d -s -c std -m vbr2pass -b 128
WMA Std 2-pass ABR 192k %s %d -s -c std -m vbr2pass -b 192
WMA Pro 2-pass ABR 192k %s %d -s -c pro -m vbr2pass -b 192
CBR 2-pass 192k (alt) %s %d -s -c std -m cbr2pass -b 192

%s makes foobar create the temporary input WAV. Neither -i nor --bufferstdin is needed. If literal - stdin is used for a Standard/Pro two-pass encode, WMAEncodeRS automatically caches the input PCM in a temporary disk file and replays it for the second pass. --bufferstdin is accepted for old command lines but has no effect and does not select RAM buffering.

Note: there is no quality-based 2-pass in WMA — 2-pass modes are always bitrate-targeted. If you want quality-based, that's 1-pass vbr with -q (e.g. - %d -s -q 90).

(For xWMA the --xwma flag is optional since the xwma extension auto-selects it, but it's harmless to include.)

Two important notes

  1. Don't pipe the output (- as output) in foobar. Use %d so foobar gives a real file path — WMA/ASF can't be written to a non-seekable pipe (the muxer must seek back to patch the header). %d is the correct, file-based path and works fine.

  2. Tags: leave foobar's converter tagging on as usual — but note WMAEncodeRS can also write tags itself if you pass e.g. --title "%title%" --artist "%artist%". You generally don't need to; foobar tags the output WMA after encoding. If you do pass them, foobar substitutes the %field% values.

Testing & fuzzing

cargo test runs unit/regression tests. The untrusted-input parsers (WAV header, xWMA, CLI) are fuzzed with cargo-fuzz (libFuzzer) — see fuzz/. Fuzzing found and fixed two robustness bugs in the WAV header parser (a divide-by-zero on a zero-channel fmt chunk, and a multi-GB allocation from an absurd fmt size), both now guarded by regression tests. Parsers are unsafe-free, so these are clean error-returns rather than memory-safety issues.

License

MIT. Original work © 2011 lvqcl; Rust port retains the same license — see LICENSE.

About

Rust port of WMAEncode. Turns WAV/PCM into fully-working .WMA or .xWMA files.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages