Version: 901a86e (reproduced 2026-09-07, Linux x86_64).
Summary
On the default (non-Dolby) ADM path, removing an audioObject -- by --adm_importance_threshold, or by the unconditional invalid-object filter -- makes every surviving object read the wrong channels of the input wav. The encode still exits 0 and writes a well-formed, decodable IAMF file. Nothing warns.
Reproduction
A four-object, four-channel default ADM whose channels carry 313, 452, 591 and 730 Hz, with importance values 10, 3, 9, 7:
encoder_main --adm_filename=./four_objects.wav --adm_profile_version=base
--adm_importance_threshold=0 --output_iamf_directory=./t0
encoder_main --adm_filename=./four_objects.wav --adm_profile_version=base
--adm_importance_threshold=5 --output_iamf_directory=./t5
Both exit 0. At threshold 0 the four per-object wav files carry 313, 452, 591 and 730 Hz, one each. At threshold 5 -- which drops only the second object -- the three surviving files carry none of their assigned tones; each is a rotation through all four input channels, including the dropped one's.
Cause
GetAudioTracksForAudioObjects (wav_file_splicer.cc) rebuilds the object-to-track map by counting sequentially over the objects that survived filtering:
for (const auto& audio_object : audio_objects) {
for (auto unused_audio_track : audio_object.audio_track_uid_ref) {
audio_tracks_for_audio_object.push_back(++audio_track_index);
The audioTrackUID is read and discarded -- see the variable's own name -- and nothing in the tree resolves a UID to a physical channel, because the chna chunk is never parsed. So removing an object renumbers every later object onto a different channel.
The read loop compounds it. Its stride is the original frame size, while each iteration consumes only the surviving tracks' bytes, so the stream cursor slips by one channel per frame. With C original channels and S surviving tracks, output sample i of the k-th survivor comes from absolute sample Si + k, i.e. channel (Si + k) % C. Because S < C that cycles through every original channel, so even the first object -- which is never renumbered -- is wrong from its second sample onward.
Impact
Any default ADM with two or more audioObjects where one is filtered produces silently wrong audio. The importance threshold reaches this path, and so does the invalid-object filter, which is unconditional -- no flag is needed. A file with three valid objects and one unsupported one in a non-last position hits this on a plain run.
Note on scope
This is the default ADM path. For Dolby-mode input the threshold has no effect on the render at all, which is #70 and is a different defect.
Suggested resolution
Record each object's first track index before the filtering pass and address channels by that, and read a whole original frame per iteration so the frame boundary stays aligned whatever was filtered. Parsing chna and resolving UIDs properly would also fix the (currently unhandled) case of non-contiguous track assignments, but is a larger change.
A patch that takes the first approach is available if it would help.
Version: 901a86e (reproduced 2026-09-07, Linux x86_64).
Summary
On the default (non-Dolby) ADM path, removing an audioObject -- by --adm_importance_threshold, or by the unconditional invalid-object filter -- makes every surviving object read the wrong channels of the input wav. The encode still exits 0 and writes a well-formed, decodable IAMF file. Nothing warns.
Reproduction
A four-object, four-channel default ADM whose channels carry 313, 452, 591 and 730 Hz, with importance values 10, 3, 9, 7:
encoder_main --adm_filename=./four_objects.wav --adm_profile_version=base
--adm_importance_threshold=0 --output_iamf_directory=./t0
encoder_main --adm_filename=./four_objects.wav --adm_profile_version=base
--adm_importance_threshold=5 --output_iamf_directory=./t5
Both exit 0. At threshold 0 the four per-object wav files carry 313, 452, 591 and 730 Hz, one each. At threshold 5 -- which drops only the second object -- the three surviving files carry none of their assigned tones; each is a rotation through all four input channels, including the dropped one's.
Cause
GetAudioTracksForAudioObjects (wav_file_splicer.cc) rebuilds the object-to-track map by counting sequentially over the objects that survived filtering:
for (const auto& audio_object : audio_objects) {
for (auto unused_audio_track : audio_object.audio_track_uid_ref) {
audio_tracks_for_audio_object.push_back(++audio_track_index);
The audioTrackUID is read and discarded -- see the variable's own name -- and nothing in the tree resolves a UID to a physical channel, because the chna chunk is never parsed. So removing an object renumbers every later object onto a different channel.
The read loop compounds it. Its stride is the original frame size, while each iteration consumes only the surviving tracks' bytes, so the stream cursor slips by one channel per frame. With C original channels and S surviving tracks, output sample i of the k-th survivor comes from absolute sample Si + k, i.e. channel (Si + k) % C. Because S < C that cycles through every original channel, so even the first object -- which is never renumbered -- is wrong from its second sample onward.
Impact
Any default ADM with two or more audioObjects where one is filtered produces silently wrong audio. The importance threshold reaches this path, and so does the invalid-object filter, which is unconditional -- no flag is needed. A file with three valid objects and one unsupported one in a non-last position hits this on a plain run.
Note on scope
This is the default ADM path. For Dolby-mode input the threshold has no effect on the render at all, which is #70 and is a different defect.
Suggested resolution
Record each object's first track index before the filtering pass and address channels by that, and read a whole original frame per iteration so the frame boundary stays aligned whatever was filtered. Parsing chna and resolving UIDs properly would also fix the (currently unhandled) case of non-contiguous track assignments, but is a larger change.
A patch that takes the first approach is available if it would help.