Skip to content

adm_to_user_metadata: gainUnit is never parsed, so a linear ADM gain is applied as dB #86

Description

@jlivingston-Cipher

Version: 901a86e (reproduced 2026-09-07).

Summary

BS.2076-2 gives the gain element an optional gainUnit attribute whose value is linear or dB, and specifies that a gain without the attribute is linear. gainUnit does not appear anywhere in this repository. The authored number is passed straight into MixGainParamDefinition.default_mix_gain, which IAMF carries in dB, so an audioObject authored 0.25 -- an attenuation of 12.04 dB -- is emitted as a boost of 0.25 dB.

Reproduction

encoder_main --adm_filename=./object_with_gain.wav --adm_profile_version=base
--output_iamf_directory=./out
probe_main --input_filename=./out/object_with_gain.iamf --format=json
with a single audioObject carrying 0.25. The bitstream reads default_mix_gain = 0.25 (default_mix_gain_q7_8 = 64). Decoding it and comparing against the identical scene with the element removed gives +0.2500 dB where the authored intent is -12.0412 dB -- an error of 12.29 dB, in the wrong direction. The encoder's own integrated-loudness figures agree: -20.582 vs -20.832 LKFS.

Cause

SetHandlerTag (xml_to_adm.cc) never inspects the element's attributes for gainUnit -- compare the position branch immediately below it, which does read coordinate from atts. SubMixAudioElementMetadataBuilder (mix_presentation_handler.cc) then quantizes the authored value directly:

if (const auto& status = FloatToQ7_8(audio_object.gain, mix_gain_q7_8);
while the renderer reads that field as dB (default_layout_renderer.cc: std::pow(10.0f, default_mix_gain / 20.0f)).

Note

This is only visible once #69 is fixed; before that, an authored object gain fails the encode outright with Inequivalent param_definition for id = 0.

Impact

Every professional ADM master that carries object gains in the BS.2076 default unit is mixed at the wrong level. A 12 dB error is not a subtlety.

Suggested resolution

Parse gainUnit, keep the value as authored alongside its unit, and convert at the consumer. Note that the two consumers convert in opposite directions: default_mix_gain is dB, while AmbisonicEncoder::SetSource takes a linear multiplier. A linear gain of 0.0 is how ADM mutes an object and has no finite dB value, so it needs a stated clamping rule rather than an encode failure.

A patch is available if it would help.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions