Some random notes about Windows Audio Processing Objects (APOs) by Etienne Dechamps.
APO stands for "Audio Processing Object". It is an API and framework designed by Microsoft for building pluggable audio filters (DSP). It is quite similar to VST in principle.
More technically, an APO takes the form of a COM class that implements the APO interfaces. The resulting class is typically provided as part of a DLL which is then registered in the system-wide COM registry (e.g. using regsvr32).
The class can then be instantiated, and each instance can process a continuous audio stream, making arbitrary changes to the audio data.
What differentiates APOs from other audio filtering frameworks (such as VST) is that it is the filtering framework used by the Windows Audio Engine.
The Windows Audio Engine is a core component of the Windows audio stack. Its role is to bridge the gap between individual application audio streams and hardware audio devices. As such it handles various tasks such as mixing audio from multiple applications, automatic format conversions, etc.
Most application audio goes through the Windows Audio Engine. In particular it will process any audio stream that is opened through the WASAPI (Shared) API (and by extension DirectSound and MME, which use WASAPI Shared internally). The only exceptions are streams opened in WASAPI Exclusive mode, Kernel Streaming (WDM-KS), and native ASIO, which all bypass the Windows Audio Engine; but these are seldom used by typical applications.
The Windows Audio Engine uses a number of APOs internally as part of its normal operation, each handling a specific task such as automatic sample rate conversion. These internal APOs notably include the (in)famous CAudioLimiter.
APOs run inside the Windows audio graph process (audiodg.exe
) which is itself
managed by the Windows Audio service (audiosrv
).
APOs can be installed as system effect APOs, or sAPOs, by implementing the
IAudioSystemEffects
interface. sAPOs are optional APOs that can be
inserted at various points inside the Windows audio engine pipeline. sAPOs can
be extremely useful because they can be used to arbitrarily filter audio for
all Windows applications running system-wide; and because they run directly
inside the Windows audio engine, they can do so in a very clean and efficient
manner with no additional conversions and zero additional latency.
sAPOs can be positioned to filter audio data at the following stages of the Windows audio pipeline:
- Stream Effect (SFX) APOs are instantiated for every application stream.
They process the audio signal as it comes in and out of a single application.
- In particular, in the playback direction, processing occurs before any mixing or sample rate conversion takes place.
- SFX APOs are the only sAPOs that are allowed to change the channel count (i.e. downmix/upmix).
- Mode Effect (MFX) APOs operate at an intermediate stage, on the mixed audio from all streams that share a common mode.
- Endpoint Effect (EFX) APOs operate on the audio signal that comes in and
out of the audio device.
- In particular, in the playback direction, processing occurs after all mixing and sample rate conversion takes place (but before CAudioLimiter).
The above describes the modern sAPO architecture as it was introduced in Windows 8.1. Previously, different kinds of APOs were used:
- Local Effect (LFX) APOs, also known as pre-mix APOs, serve the same purpose as SFX APOs.
- Global Effect (GFX) APOs, also known as post-mix APOs, serve the same purpose as EFX APOs.
LFX and GFX APOs can still be used in modern versions of Windows, but Microsoft does not document them anymore; they should be considered deprecated. If both modern (SFX/MFX/EFX) and legacy (LFX/GFX) sAPOs are configured, then Windows will use the modern ones.
sAPOs are configured on a per-audio endpoint device basis. That is, each audio device uses its own set of APOs.
Which APOs to use is normally decided by the audio device driver; specifically, they are defined in the audio driver INF file.
An audio device driver can decide to use some of the sAPOs that are bundled
with Windows, or it can implement its own custom sAPO and
include it in the driver package. Keep in mind, however, that APOs always run in
user mode (in the audiodg.exe
process), not in kernel mode; they are merely
bundled with the driver, and are not part of the kernel-mode driver module
itself.
When an audio driver is installed, any custom sAPOs are installed and registered, and the sAPO configuration from its INF file is copied to the Windows Audio Engine configuration (see below).
After the audio driver is installed, it is technically possible to go in and change the Windows Audio Engine configuration after the fact to use different sAPOs, even third-party sAPOs that are not part of Windows nor the original driver. This is extremely powerful because that makes it possible to apply any arbitrary audio filtering to any audio device, independent of driver (think "system-wide VSTs"). This is precisely how Equalizer APO works. Note, however, that this approach to setting up sAPOs is not officially supported by Microsoft; the fact that it works should be considered an "happy accident". Also note that such custom configuration will be overwritten every time the audio driver is reinstalled, and that some Windows updates have a tendency to reinstall audio drivers.
All commands shown in this section are Powershell commands. Commands that make changes will only work when run as Administrator.
The configuration for the Windows Audio Engine lives under the following Windows registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio
Playback devices are found under the Render
key. Recording devices are found
under the Capture
key.
Under Render
and Capture
, each audio endpoint device has its own
key, named after the endpoint GUID (see below).
Under each endpoint device key, the FxProperties
key contains system effect
APO configuration.
For example, the following key contains system effect APO configuration for the
playback endpoint device with GUID {b39fc22d-4c5d-4e65-8276-db7f999d2d06}
:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render\{b39fc22d-4c5d-4e65-8276-db7f999d2d06}\FxProperties
The initial configuration comes from the driver store, which is itself populated from the audio device driver INF file.
One way is to determine the GUID of an endpoint device is to open the Equalizer APO Configurator, select a device, and click on "Copy Device command to clipboard". The copied command includes the endpoint GUID.
Otherwise, you can use the following command:
Get-PnpDevice -Class AudioEndpoint | Format-Table -Property InstanceId,Present,FriendlyName
Example output:
FriendlyName Present InstanceId
------------ ------- ----------
Headset (CORSAIR VIRTUOSO Wireless Gaming Headset) False SWD\MMDEVAPI\{0.0.0.00000000}.{A8079308-1956-4FB7-BBB0-8D8842611102}
Headset Microphone (CORSAIR VIRTUOSO Wireless Gaming Headset) False SWD\MMDEVAPI\{0.0.1.00000000}.{4ED8903D-2C88-4EAC-93E4-39B0BFE7475C}
Monitors (Xonar U7) True SWD\MMDEVAPI\{0.0.0.00000000}.{5D16E8DA-37A3-4657-8D52-57FFE97F4EF9}
S/PDIF 1 (Virtual Audio Cable) False SWD\MMDEVAPI\{0.0.1.00000000}.{42BA34C2-E6E7-452D-BCE3-ADCBEC30E1A4}
Line 1 (Virtual Audio Cable) False SWD\MMDEVAPI\{0.0.0.00000000}.{185D6665-3115-45BD-9E85-39BA4E326DDD}
The InstanceId
is normally SWD\MMDEVAPI\
followed by the
endpoint ID string. The endpoint ID string is itself composed of a
{0.0.0.00000000}.
or {0.0.1.00000000}.
prefix (denoting Render and Capture
endpoints, respectively), followed by the endpoint GUID. So in the above
example, Monitors
is a Render endpoint device and its GUID is
{5D16E8DA-37A3-4657-8D52-57FFE97F4EF9}
.
Endpoint GUIDs will change if the audio driver is reinstalled. Note that some Windows updates reinstall all audio drivers as a side effect.
In code, the proper way to enumerate audio endpoints is to use
IMMDeviceEnumerator::EnumAudioEndpoints()
.
The FxProperties
registry key holds the system effect APO configuration for a
specific audio endpoint device.
The configuration is organized as a set of registry values, which correspond
to individual properties. A property is identified by a GUID, followed by a
comma ,
, followed by a number. For example:
{b725f130-47ef-101a-a5f1-02608c9eebac},10
.
The official list of supported system effect APO properties can be found in the
audio driver INF settings documentation (look for properties
that are installed under HKR,FX\0
in the INF File Sample). Alternatively,
the audioenginebaseapo.idl
file in the Windows SDK contains a more
exhaustive list (e.g. it includes legacy LFX/GFX properties).
The most important properties are those that control which sAPOs are used. These are:
Type | Property ID | Property name |
---|---|---|
SFX | {d04e05a6-594b-4fb6-a80d-01af5eed7d1d},5 |
PKEY_FX_StreamEffectClsid |
MFX | {d04e05a6-594b-4fb6-a80d-01af5eed7d1d},6 |
PKEY_FX_ModeEffectClsid |
EFX | {d04e05a6-594b-4fb6-a80d-01af5eed7d1d},7 |
PKEY_FX_EndpointEffectClsid |
LFX | {d04e05a6-594b-4fb6-a80d-01af5eed7d1d},1 |
PKEY_FX_PreMixEffectClsid |
GFX | {d04e05a6-594b-4fb6-a80d-01af5eed7d1d},2 |
PKEY_FX_PostMixEffectClsid |
If any of SFX, MFX or EFX are present, then LFX and GFX are ignored.
All properties are optional. If no properties are present, or if the
FxProperties
key is absent entirely, then no system effect APOs are used.
Another notable property is PKEY_AudioEndpoint_Disable_SysFx
({1da5d803-d492-4edd-8c23-e0c0ffee7f0e},5
, DWORD), which, if set to 1,
disables all sAPOs. It is mapped to the "Enable audio enhancements" checkbox in
the Windows audio device settings.
The string value of a PKEY_FX_*EffectClsid
property is a CLSID which
identifies the specific APO COM class to instantiate to filter the audio signal.
CLSIDs are globally unique and chosen by the sAPO developer, so they can be
expected to be stable even across different machines and OS versions.
When a sAPO is installed, it is registered in the system-wide COM class store
found at HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
so that it can be looked up
by its CLSID.
For example, the following command will look up information about CLSID
{EC1CC9CE-FAED-4822-828A-82A81A6F018F}
which is the Equalizer APO GFX
sAPO:
$RegistryKey = Get-Item "HKLM:\SOFTWARE\Classes\CLSID\{EC1CC9CE-FAED-4822-828A-82A81A6F018F}"
$RegistryKey.GetValue("")
$RegistryKey.OpenSubKey("InprocServer32").GetValue("")
Example output:
EqualizerAPO Post-Mix Class
C:\Program Files\EqualizerAPO\EqualizerAPO.dll
In this example EqualizerAPO.dll
is the DLL that contains the sAPO code. The
Windows Audio engine audiodg.exe
process will load that DLL to instantiate the
filter and process the audio.
The following example command will delete all registry values directly under
FxProperties
for the {2f716148-66dd-4afe-9698-d3c74eea039a}
endpoint GUID,
thus removing all sAPO configuration for that endpoint:
$RegistryKey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey(
"SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render\{2f716148-66dd-4afe-9698-d3c74eea039a}\FxProperties",
[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree,
[System.Security.AccessControl.RegistryRights]::QueryValues -bor [System.Security.AccessControl.RegistryRights]::SetValue)
$RegistryKey.GetValueNames() | ForEach-Object { $RegistryKey.DeleteValue($_) }
You can use the Registry Editor to make a backup of the contents of the registry key before running the above command so that you can restore them later. Another way to revert to the default state is to reinstall the audio driver.
The same result can be achieved by deleting or renaming the FxProperties
key,
but permissions might get in the way (see below).
By default, special permissions apply to the overall MMDevices
registry key.
The only user with full control of that key is TrustedInstaller
. Even
Administrators have restricted permissions: they can add, modify and remove
registry values, but they cannot change the keys.
This can prevent certain useful operations on the FxProperties
key, such as
deleting or renaming it, or even creating it in the first place (which is
necessary in order to add sAPOs to an endpoint that never had any).
Fixing the permissions from the command line is surprisingly hard for a number
of silly technical reasons. Namely: there is no easy way to run commands under
the TrustedInstaller
user or to enable the TakeOwnership
privilege
without the help of external tools, and built-in tools such as takeown
cannot be used on the registry.
However, it is possible to fix the permissions manually using the Registry
Editor (regedit
):
- Navigate to
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio
. - Open the permissions of the
Audio
registry key and change its owner to the Administrators group. - Give the Administrators group Full Control over the registry key.
You should then be able to make any change you like, including creating,
deleting and renaming FxProperties
keys.
The relevant registry entries are consumed by the Windows Audio service
(audiosrv
). To make sure changes are picked up, the service usually needs to
be restarted:
Restart-Service -Name audiosrv
As previously explained, APOs run inside the audiodg.exe
process which itself
runs under the Windows Audio Service (Audiosrv
).
This service, like virtually all services, does not run under your normal Windows user account. Instead, its access token gives it permissions that are roughly equivalent to the LocalService account.
Most notably, for security and isolation reasons, the service does not have
access to your user directory (i.e. C:\Users\<username>
).
In some cases this can lead to an APO misbehaving because it is attempting to open a file (or other securable object, such as a registry key) that it is not allowed to access.
One example is attempting to store an Equalizer APO configuration file
inside a user directory. The Equalizer APO Configuration Editor will display a
helpful warning in this case, and the APO itself will log an error message into
its logfile (C:\Windows\ServiceProfiles\LocalService\AppData\Local\Temp\EqualizerAPO.log
).
In other cases the issue might be harder to troubleshoot. For example, most VSTs do not anticipate running into permission issues. When such VSTs are used inside Equalizer APO (for example) and run into unexpected access issues, the resulting behaviour can be erratic and hard to troubleshoot.
One way to confirm that an APO is having trouble accessing specific objects is to use Process Monitor. Use the following filters:
- "Process Name" is
audiodg.exe
- "Result" is
ACCESS DENIED
Then restart the Windows Audio service and start streaming audio. Access denied errors should then appear in Process Monitor.
The following example shows Equalizer APO attempting to open an inaccessible configuration file:
It is possible to determine which DLL is making the offending calls by looking
at the event stack in Process Monitor. In this example it is
EqualizerAPO.dll
, as expected. If the failure originates from a VST used
within Equalizer APO, the stack should mention the offending VST DLL alongside
(or instead of) Equalizer APO.
Some issues can be more subtle. For example some VSTs might expect to find files or registry entries under the current user profile. These objects might exist under your personal user profile, but not in the local service user profile. To troubleshoot such issues you might need to widen your search and expand your Process Monitor filters to include more failure modes, such as "not found" errors.
There are two ways to fix this issue:
- Move the offending files/objects in a location that the Audio service can access. For example Program Files or ProgramData. Or:
- Change the permissions (DACL) on the offending files/objects to allow access by the Audio service.
To adjust the permissions, go to the Security properties of the offending
file/object (or a parent) and ensure the NT SERVICE\Audiosrv
user principal
has access. (This principal is the service SID. You can also use the local
service account, but only allowing the Windows audio service is cleaner.)
Then restart the Windows Audio service.
Note: while this technique can be used to store Equalizer APO configuration
files outside of the standard config
directory, keep in mind that doing so
will break the Equalizer APO "instant mode" feature (i.e. live changes) because
Equalizer APO only watches its config
directory for changes.
- Windows Audio Architecture
- Audio Processing Object Architecture
- Audio INF File Settings
- Equalizer APO, notably the developer documentation
- Audio Endpoint Devices
- Custom Audio Effects in Windows Vista (notably explains the meaning of the deprecated LFX and GFX APO placements)
- Enumerate APOs