Skip to content

Apply an audio taper to endpoint volume - #650

Merged
mdias merged 2 commits into
mdias:masterfrom
L0FKA:fix/endpoint-volume-taper
Oct 26, 2026
Merged

mdias merged 2 commits into
mdias:masterfrom
L0FKA:fix/endpoint-volume-taper

Conversation

@L0FKA

@L0FKA L0FKA commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Apply an audio taper to endpoint volume

Rocksmith adjusts guitar input gain through IAudioEndpointVolume::SetMasterVolumeLevelScalar: 0.17 at start, a
0.40..0.50 search during calibration, then 1.0. RS_ASIO uses that scalar directly as a linear amplitude
multiplier. A Windows endpoint scalar follows an audio taper, so the same setting gives very different
attenuation: the linear multiplier is +11.4 dB too hot at 0.17 and +4.5..5.9 dB too hot at 0.40..0.50.

What changes

  • Endpoint taper: the endpoint scalar maps to amplitude as scalar^1.74, fitted to measurements of a real
    Windows software endpoint (MOTU M Series), within 0.3 dB of every measured point. At 0.17 the attenuation
    becomes -26.78 dB instead of -15.39 dB.

  • Unity stays unity: the taper does not change the gain at scalar 1.0, which stays 0 dB. It does not fix
    distortion while the endpoint is at full scale. SoftwareMasterVolumePercent keeps its linear meaning.

  • Integer software volume: Int16, packed Int24 and Int32 retain whole-percent integer scaling in the callback.
    This avoids converting each sample to floating point and back on a performance-sensitive path. The 1% step is
    an intentional trade-off; integer tenths-of-a-percent scaling can be considered if testing shows it is needed.

  • Endpoint API: implements:

    • master volume in dB, get and set;
    • the range (-96..0 dB, 1.5 dB steps), with no digital gain above unity, since the player sets the level with
      the interface's analog gain;
    • channel count;
    • volume steps;
    • QueryHardwareSupport (0, software only).

    Invalid and NaN settings are rejected. With software endpoint volume disabled, the new queries report
    E_NOTIMPL, so no range is advertised that the setters would refuse. Mute, per-channel volume and
    notifications stay unimplemented.

  • Audio path: the taper is computed in the setter and cached. The audio callback reads the cached amplitude
    without locking. Setters are serialized, so the reported level and the applied gain can't drift apart under
    concurrent calls. Applies to capture and render.

Testing

  • Offline: standalone taper checks covered the measured curve, round trips, the floor, dB quantization and
    steps. A deliberately wrong exponent fails them. Release x86 builds.

  • Live capture readout (temporary instrumentation in a validation-only build, not part of this PR):
    September 2022 build, MOTU M Series Int32LSB, endpoint scalar 0.17.

    Build Before volume After Gain
    master -12.00 dBFS -27.39 -15.39 dB (linear)
    this PR, whole-percent integer DSP -12.00 -39.96 -27.96 dB

Not established: improved calibration or scoring with a real guitar, long sessions, or equivalence to every
device's taper. This is one software-endpoint curve, measured on one interface. It does not change the
interface's analog gain.

… its dB surface

Rocksmith sets the capture endpoint's IAudioEndpointVolume scalar while
calibrating guitar input (0.17 at start, a 0.40..0.50 search ramp, then 1.0).
A Windows endpoint scalar is a fader position mapped to dB through a taper,
but RS_ASIO applied it as a linear amplitude, so the input was too hot below
1.0: +11.4 dB at 0.17 and +4.5..5.9 dB at 0.40..0.50. Measured on a real
endpoint, amplitude = scalar^1.74 (within 0.3 dB of every measured point).

- VolumeTaper.h: scalar <-> dB <-> amplitude, quantized to a 1.5 dB step.
- The amplitude is computed in the setter, off the audio callback; the scalar,
  the amplitude and the master volume are atomics. Master volume stays linear.
- IAudioEndpointVolume now implements GetVolumeRange (-96..0 dB, 1.5 dB),
  Get/SetMasterVolumeLevel (dB), GetVolumeStepInfo, VolumeStepUp/Down,
  QueryHardwareSupport (0) and GetChannelCount. The range tops out at 0 dB:
  no digital makeup gain, level is set with the interface's analog gain.
- With software endpoint volume disabled these report E_NOTIMPL, so no range
  is advertised that the setters then refuse.
- NaN is rejected by the scalar and dB setters.
@mdias

mdias commented Oct 26, 2026

Copy link
Copy Markdown
Owner

The reason integer volume is applied in whole percents instead of precisely is performance. Being off by 1% is not going to get noticed, but if we're at the limit of what the system can push, converting the samples from integer to floating point, multiplying and then converting back to integer might be the thing that generates an xrun.

The whole-percent math is preferable. And if 1% is not enough precision we could multiply and divide by 1000 to get 0.1% accuracy.

The scalar and its precomputed amplitude are separate atomics, so two
concurrent setters could interleave and leave the reported level and the
applied gain permanently different (e.g. 0.17 reported, 0.50 applied). A
mutex taken only by the setter keeps them a pair; the audio callback still
reads the amplitude without locking.
@L0FKA
L0FKA force-pushed the fix/endpoint-volume-taper branch from c7a8ce2 to 6aa0643 Compare October 26, 2026 20:46
@mdias
mdias merged commit 47e8d18 into mdias:master Oct 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants