Skip to content
Score2 edited this page Aug 15, 2026 · 2 revisions

Volume

Across an AudioHub peer link there are two things that could be kept in step: the position of the slider, or the loudness it produces. They cannot both be kept in step. This page shows why, and says which one AudioHub keeps.

AudioHub keeps the position. Move a control to 50 % and the peer's device lands on 50 % too — and at that percentage the two machines are not equally loud. The difference is measured below, and it is what 1.0.0 does; it is not a bug that has been fixed. (The one place even the percentages diverge is a Windows virtual device — see Mode B.)

AirPlay is governed by a different contract; see AirPlay volume is a separate contract.


What travels between the two machines

On the wire one number in 0..1, plus an optional mute flag
Where it starts Mode A: the volume slider on the peer card (and the macOS menu-bar slider). Mode B: the system's own volume control on the AudioHub virtual device
How the receiving side reads it as a slider position — it is written straight to the default output device's scalar volume property: kAudioDevicePropertyVolumeScalar on macOS, SetMasterVolumeLevelScalar on Windows
What the audio stream carries nothing volume-related; samples are sent at full scale (one exception)

No decibel conversion happens between the two machines. Both platforms expose a native dB property for a real device, and the shipped code does not use it — which is exactly why loudness is not preserved.

The mute flag is optional and separate. Dragging the slider in AudioHub sends the level only and leaves the peer's mute control alone, so a slider drag can never unmute a machine somebody deliberately muted; the mute button sends it explicitly. A virtual device in Mode B reports both together, because the system control it mirrors carries both.


The consequence, measured

Same slider position, two machines, real hardware on both ends — macOS built-in speakers against a Windows output endpoint:

Slider macOS Windows Difference Perceived
0 % −63.5 dB −96.0 dB −32.5 dB quieter
10 % −43.4 dB −34.8 dB +8.7 dB ~1.8× louder
25 % −31.8 dB −21.0 dB +10.8 dB ~2.1× louder
50 % −18.6 dB −10.5 dB +8.1 dB ~1.8× louder
75 % −8.5 dB −4.4 dB +4.1 dB ~1.3× louder
90 % −3.3 dB −1.6 dB +1.7 dB ~1.1× louder
100 % 0 dB 0 dB 0 dB equal

Only 0 % and 100 % line up. Everything between them differs, and the worst error falls in the 10–50 % range people actually use — where Windows is roughly twice as loud as macOS at the same slider position.

Practically: after you drag a slider, check the far end by ear, not by reading the percentage back.

There is no single "macOS curve"

A tempting framing is macOS uses one taper, Windows uses another. The measurements do not support it. Three different mappings across the four devices measured, two of them on macOS:

Device Mapping At 50 %
macOS built-in speakers dB = −63.5 · (1 − √s) −18.6 dB
AudioHub's own macOS virtual device dB = −63.5 · (1 − √s) −18.6 dB
A third-party macOS virtual cable dB = −64 + 64·s (dB-linear) −32.0 dB
Windows software endpoint amplitude = s^1.745 −10.5 dB

Square root, dB-linear, and a power law: the curvature does not even point the same way. Windows is internally inconsistent too — an endpoint with hardware volume follows a further curve of its own.

AudioHub's macOS virtual device is the one row here that is a deliberate choice rather than a vendor's: it is set point-for-point to Apple's built-in taper, so that a volume key press on it steps by the same number of decibels as a volume key press on the built-in output.

One structural difference is worth noting: the Windows mapping is independent of the device's dB range, while the macOS mapping scales with it. These are two design philosophies, not two parameterisations of one idea.


Why this is a choice and not a bug

Let T_ours be the taper of the virtual device AudioHub controls, and T_peer the taper of the peer's real device, which the hardware vendor and the OS control.

  • Equal loudness requires T_ours(s₁) = T_peer(s₂).
  • Equal slider numbers requires s₁ = s₂.

Both hold simultaneously if and only if T_ours ≡ T_peer. Measurement has already shown T_peer is several different functions across several devices, so no single T_ours can equal all of them. This is not an engineering problem where a better implementation gets you both; one has to be chosen.

Equal loudness would mean putting an amplitude on the wire and landing it through each platform's native dB property, which is a design that exists on paper and is not in 1.0.0. Until it ships, the numbers agree and the loudness does not.


Volume in each mode

Which of the three sets of rules below applies depends on the mode this machine is in, and the modes are mutually exclusive — see Operating Modes for what each one does and where the picker is.

Mode B · Virtual devices

The mode itself, including what appears in the system device list and what happens when the driver is missing, is described in Mode B · Virtual devices.

There is no volume slider on the peer card in Mode B — the control is the system's own. Adjust the AudioHub virtual speaker in Sound settings, with the volume keys, or from any app's volume UI, and the value is written to the peer's real default output device.

The virtual device never scales samples; it is a control node only. Whatever attenuation you hear happens on the peer's device, at the peer's taper.

The two virtual devices differ in one way that shows on screen. The macOS virtual device publishes the slider position directly, so its percentage matches the peer's. The Windows virtual device carries volume as decibels internally — that is what a kernel-streaming volume node holds — so both directions pass through a dB conversion and the two percentages will not line up. Measured on a real Windows endpoint, a peer sitting at 50 % lands at about 67 % on the Windows side. Treat the exact figure as an expectation rather than a guarantee: it has never been read back through the AudioHub driver itself.

Mode A · Driverless

The mode itself, including the capture backends and the one-peer-at-a-time rule, is described in Mode A · Driverless.

Two independent things, and they are easy to confuse:

Where Default What it does
Peer volume slider the peer's card (and the macOS menu-bar slider) drives the peer's real output device
Sync volume Settings › Options off makes this machine's system output follow the peer's reading
Mute locally Settings › Options off mutes this machine's output once, when a speaker path is established

The Options card only shows these while Mode A is the selected mode.

Sync volume puts the peer in charge: on connect this machine adopts the peer's value, and if both change at once the peer's value wins.

Mute follows volume — with one exception. If Mute locally is also on, only the volume level syncs and mute stays local; otherwise the one-shot mute would travel down the sync link and silence the peer as well. The app states this under the switch when both are on.

That local mute is a one-time action, not a maintained state. It fires once when the speaker path comes up. Nothing re-applies it, so if you unmute afterwards that is taken to mean you want this machine audible too, until the next path is established. It is skipped entirely on a capture backend that reads the stream after the output volume — Windows Device loopback — because muting there would take the mirror being sent to the peer down with it.

Share

Share mode is the default on a fresh install, so this is the section that applies until someone changes the mode.

Nothing on this page applies to the serving side. A machine in Share mode owns the output device the peer is driving; it has no outbound speaker stream of its own and no peer volume control. The Stats tab labels its own device's reading Local volume rather than Peer volume for exactly this reason.


When the peer's device has no volume at all

Some outputs expose no volume control whatsoever. The usual case is a macOS aggregate or multi-output device built in Audio MIDI Setup: it has no master volume, so there is nothing on it for a slider to drive.

In Mode B, AudioHub takes the volume over. The value stops being sent to the peer and becomes a software gain applied on the sending side, so the wire then carries audio that already has volume in it. Two things you will see:

  • the line under the peer's volume slider reads "Peer volume unsupported · Using local software gain.";
  • the level starts at 100 % the moment the fallback engages. That is not a jump in loudness — at that instant nothing anywhere is attenuating, so 100 % is the honest reading. Adopting the peer's reported value instead would mute the link, because a device with no volume reports 0.

While the fallback is in force the slider is a straight multiplier: 50 % is half the amplitude, or −6 dB. That is a much shallower taper than any real device's — the same 50 % is about 12.6 dB louder than it would be through a Mac's built-in output. Expect to sit lower on the slider than you are used to.

Mute is folded into the same gain, and both ride a 20 ms ramp, so neither pops. Any gain below unity also switches on TPDF dither for the 16-bit quality rungs, because attenuating in 16 bits throws away resolution; the 32-bit-float and 24-bit rungs are already wide enough and get none. At exactly 100 % the wire is bit-for-bit identical to the normal full-scale path.

In Mode A there is no fallback. Mode A has no virtual device to take the volume over, so against such a peer the card's slider is greyed out and the Sync volume switch does nothing in either direction. The peer's reported "0" is an absence, not a level, and following it would write silence onto a working speaker.


Audio is sent at full scale

The stream itself carries unmodified, full-scale audio. Volume is a control-plane property applied at the endpoints, not something mixed into the samples in transit.

The software-gain fallback above is the one exception, and the interface says so rather than leaving a slider that silently does nothing.


AirPlay volume is a separate contract

AirPlay receiving has its own product and protocol semantics, and they are deliberately not the peer-link semantics above. For the receiver itself — turning it on, the password, and where the sound goes — see Share Protocols. Its -30..0 dB volume field is treated as a linear slider coordinate, not as acoustic gain, and is mapped directly onto the receiving system's slider:

  • sender to receiver: system_scalar = (clamp(airplay_db, -30, 0) + 30) / 30;
  • receiver to sender: airplay_db = -30 + 30 * clamp(system_scalar, 0, 1).

-144 is the mute value, and the two directions treat it differently:

  • Inbound -144 both mutes this machine's output and moves its visible slider to zero. Apple's own sender uses -144 for the visible zero-percent endpoint, not only for a separate mute button, so leaving a 50 % slider behind would break the direct-agreement contract. AudioHub reports failure if the slider does not actually reach zero.
  • A mute performed here on the receiver is reflected back as -144 without disturbing the local slider, so an explicit mute/unmute cycle returns to where it was.

An unmuted scalar of 0 maps to -30, which is not mute.

Do not reason about AirPlay volume through the peer-link table on this page, and do not expect it to run through the endpoint's native dB taper. Converting it once would put a client's 0 % at roughly 10 % on the receiver and collapse the bottom of the range into silence.


When the slider does nothing

Symptom Likely cause What to do
Slider and mute button are greyed out, note reads "Peer volume unsupported" the peer's default output has no volume control, and you are in Mode A switch the peer's default output to a device that has a volume control, or use Mode B, where AudioHub takes the volume over
Note reads "Peer volume unsupported · Using local software gain" Mode B fallback is in force working as intended; the slider is real and drives a send-side gain, starting at 100 %
Slider moves, far end does not the peer accepted the value but its device refused the write this is only recorded in the service log; check the peer's output device directly
Sync volume is on and this machine's output never follows the peer's device reports no adjustable volume, so the switch is inert see the first row — there is no software-gain fallback in Mode A
Percentages agree but one machine is much louder expected — see the measured table set by ear, not by percentage
Note reads "Sync off" or "Loading volume…" the session is not carrying volume yet, or no reading has arrived wait for the session to establish; if it persists, reopen the speaker path

A note on measurement method

If you go looking at this yourself: on macOS, the Core Audio properties that translate between scalar and decibels do not agree with the device's own behaviour. On built-in speakers the translation property reports a linear curve that contradicts the device's actual state by more than 15 dB, and on virtual devices it simply returns an error.

Trust only set-then-read-back. Every figure on this page was obtained that way, on real hardware on both platforms.

One path has not been measured end to end: what the Windows audio engine derives from the AudioHub virtual device's own volume node. The driver's half of that conversion — decibels to and from a 16.16 scalar — is pinned by tests against the formula, but the engine's half has only been measured on an ordinary Windows endpoint, never on one fed by the installed AudioHub driver.

Clone this wiki locally