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.
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_definitionfor 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.