-
Notifications
You must be signed in to change notification settings - Fork 0
Audio Quality
The wire format is chosen from a six-rung ladder. One rung fixes two parameters at once: sample rate and bit depth.
Every rung is mono. There is no stereo rung and no stereo setting. Capture downmixes to one channel before anything reaches the ladder — microphone capture averages the device's channels, system-audio capture sums the front pair — and playback writes that one channel to every channel of the output device. This matters most where the numbers below look familiar: 1536 kbps is the same figure as CD-grade 48 kHz / 16-bit stereo, but here it buys one channel at higher precision, not two.
Rung 0 is the best. The number increases as quality decreases, which matches the direction AUTO moves when a link degrades.
| Rung | Format | Audio bitrate | Wire packetisation | In the app | Setting id |
|---|---|---|---|---|---|
| 0 | 48 kHz · 32-bit float | 1536 kbps | 2 × 5 ms | PCM 48 kHz · Float32 — Float precision | pcm48k32f |
| 1 | 48 kHz · 24-bit | 1152 kbps | 2 × 5 ms | PCM 48 kHz · 24 bit — High precision | pcm48k24 |
| 2 | 48 kHz · 16-bit | 768 kbps | 1 × 10 ms | PCM 48 kHz · 16 bit — Full-band · Recommended | pcm48k16 |
| 3 | 32 kHz · 16-bit | 512 kbps | 1 × 10 ms | PCM 32 kHz · 16 bit — Balanced | pcm32k16 |
| 4 | 24 kHz · 16-bit | 384 kbps | 1 × 10 ms | PCM 24 kHz · 16 bit — Voice priority | pcm24k16 |
| 5 | 16 kHz · 16-bit | 256 kbps | 1 × 10 ms | PCM 16 kHz · 16 bit — Clear voice | pcm16k16 |
Three Opus rungs (64 / 128 / 256 kbps) are drawn on the slider but cannot be selected. The app marks them unavailable and gives the reason: this build was compiled without libopus. That is a property of the binary, not a setting — there is no option, plug-in or codec pack that enables them, and the service rejects the values outright rather than substituting a nearby rung. They are drawn rather than hidden so that "why does my machine not have that option" has a visible answer.
Bitrate is one channel's audio payload only — rate × depth ÷ 1000,
excluding protocol overhead. Rungs 0 and 1 carry proportionally more overhead
because they send two packets per 10 ms rather than one, so do not
reverse-engineer measured bandwidth from this column.
No rung causes IP fragmentation. The largest IP datagram any rung produces is 1044 bytes, which stays comfortable even against a 1420-byte tunnel MTU. A test holds every rung to that budget, so a future rung cannot quietly break it.
The two get confused constantly, so the UI states both.
Usable audio bandwidth is half the sample rate (the Nyquist limit). A 48 kHz wire rate carries content up to 24 kHz. Both numbers are correct and they differ by a factor of two — a card showing "48 kHz" and a detail panel showing "24 kHz" are agreeing with each other, not contradicting.
Also easy to misread: 16 kHz is a sample rate; 16 bit is a bit depth.
Every rung label writes both dimensions in full for exactly this reason.
Bit depth does not affect bandwidth. It sets the quantisation precision of each sample, which shows up as noise floor, not as frequency range. 16-bit is CD precision, 24-bit is the standard interchange depth between professional audio devices, and 32-bit float is the pipeline's own internal format — at that rung the wire stage performs no quantisation at all, because the codec degenerates to a byte-order copy.
Rungs ascend by bitrate, which means sample rate is bought up to 48 kHz first, and only then is bit depth bought.
Low rate combined with high depth never appears on the ladder, because it is never a good trade. 48 kHz is the only rate that avoids resampling entirely, and this pipeline's resampler is linear interpolation without an anti-aliasing low-pass. Giving up 48 kHz to gain bit depth trades roughly 90 dB of damage for 48 dB of improvement.
Read from rung 0 downward, the ladder therefore sheds bit depth first (inaudible) and only then sheds bandwidth (audible). That behaviour is not a second rule set; it falls out of the ordering.
Only two values on the bit-depth axis are industrially meaningful — 24-bit as the standard interchange depth, and 32-bit float as the native pipeline format — which is why one sample rate occupies three consecutive rungs.
AUTO moves one rung at a time. The rules differ by connection method, because the signals available on a TCP path are not the ones available on a UDP path. The statistics period is one second in both cases.
Driven by packet loss and jitter reported back by the receiver:
| Direction | Condition |
|---|---|
| Down one rung | loss > 5 % or jitter > 15 ms — acts immediately |
| Up one rung | 10 consecutive clean periods, where clean means loss < 0.5 % and jitter < 5 ms |
The asymmetry is the hysteresis. A single bad period drops a rung immediately; a rise requires an unbroken run. Any period that is neither clean nor bad resets the streak to zero rather than counting toward it.
Loss and jitter are not used here. Jitter is systematically understated, because TCP's failure mode is "pause, then deliver in a burst" and within a burst the frame-to-frame differences are near zero. Loss does exist on TCP — it is manufactured by the send-side staleness gate, and has been measured climbing from 6.1 % to 41.7 % on an overloaded link — but it cannot arrive before the first audible gap, so demoting on it would mean waiting to sound bad first.
| Direction | Condition |
|---|---|
| Down one rung | this machine's send-queue backlog above 20 ms for 3 consecutive periods |
| Up one rung | 10 consecutive clean periods: backlog ≤ 10 ms, the receiver's one-way delay spread under 200 ms (half the degraded buffer's deepest target), and the staleness gate did not drop a frame this period |
Demotion needs three periods rather than one because a single TCP retransmission can hold a frame for 200–300 ms, and retransmission is normal on a link that only carries TCP — reacting to each one would walk the ladder to the bottom in seconds. One period under the line resets the streak.
The backlog reading is local, so on a degraded path AUTO does not wait for the peer's report to come back. Details of what degradation costs are in Connection method.
On a direct path AUTO starts at and never climbs above rung 2 (48 kHz · 16-bit).
The reason is bandwidth courtesy: rung 1 costs 50 % more bandwidth than rung 2 and rung 0 costs 100 % more, for a difference that is not audible. Doubling every AUTO user's bandwidth without being asked is not a reasonable default.
On a degraded path AUTO both starts at and is capped at rung 3 (32 kHz · 16-bit) — one rung lower in both respects, not just capped lower. Rungs 0 and 1 suffer disproportionately there and having sound at all comes first. The same AUTO setting therefore lands on different rungs on different paths, by design.
So AUTO is best understood as a "don't waste bandwidth" control. Rungs 0 and 1 are there for when you explicitly want them.
Choosing any fixed rung stops the ladder for that direction. The rung will not move again, however bad the link gets, until you set it back to AUTO — the ladder's accumulated state is discarded rather than paused, so switching back starts learning from the top again.
That is the point of a fixed rung, but it is worth saying out loud: pin rung 0 (1536 kbps) on a link that cannot carry it and you get permanent dropouts, with nothing stepping down to rescue you.
Rungs 0 and 1 cannot fit a whole 10 ms frame's payload into one datagram, so they are split into two 5 ms packets on the wire. The frame length itself does not change.
The receiver pairs the halves by sequence number. If the partner packet has not arrived by the time the next frame's packet does, it delivers a half-frame concealment rather than waiting — there is no timer, because waiting would convert a bandwidth problem into a latency problem.
This needs its own counter: a half-frame concealment delivers something that looks like a complete frame, so the jitter buffer does not record it as concealment, and without a separate count "this rung is losing half its packets" would be invisible.
The Stats tab counts them, in the per-session detail, as Half-frame fixes — and only once the count is above zero, so on a healthy link the row is simply absent. A few are inaudible. Continuous growth means the link is losing packets; dropping one rung resolves it, since rung 2 and below send a single packet per frame and cannot be half-lost. To see which rung a stream is actually running, read the session's Quality metric on the same tab — under AUTO the rung is not something you set, so the setting will not tell you.
Quality is set per peer, per direction, in the Connection card on that peer's detail page — Receive quality and Send quality. There is no global slider.
Latency is executed by the receiving end and quality by the sending end, so the two settings on one row physically take effect on two different machines. The one that has to cross the wire is pushed to the peer, which complies. You still set all four from this one page:
| You set | Executed by | How it gets there |
|---|---|---|
| Receive latency | this machine's jitter buffer | local |
| Receive quality | the peer's ladder | pushed to the peer |
| Send latency | the peer's jitter buffer | pushed to the peer |
| Send quality | this machine's ladder | local |
These four apply to sessions this machine opens — that is, when it is the consumer, in Mode A or Mode B. When the peer opened the session, the peer's values govern both directions and yours are stored but inert. That is always the case in Share mode, which is the default on a fresh install, so the commonest first-run state is one where these controls save faithfully and change nothing. The app says so on the card: "This device is in Share mode. The peer using it (…) sets the send and receive profiles; the requested values are shown here."
The Stats tab keeps a read-only overview of every peer's four settings — the only place where "which peer is still on AUTO" is visible at a glance. It is not on the Settings page; the Settings page has no latency or quality control at all.
Start here
Using AudioHub
- Window and tray
- Operating Modes
- Virtual Driver
- Discovery and Pairing
- Peer detail page
- Share Protocols (Sharing tab)
- Web Access
How it behaves
Reference
- Settings Reference
- Platform Notes
- Firewall and network
- CLI Reference
- Versions and Compatibility
- Glossary
- Licenses and Attribution
Troubleshooting and removal
This wiki describes AudioHub 1.0.0.