| /* |
| * Copyright (c) 2024, Alliance for Open Media. All rights reserved |
| * |
| * This source code is subject to the terms of the BSD 3-Clause Clear License |
| * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear |
| * License was not distributed with this source code in the LICENSE file, you |
| * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the |
| * Alliance for Open Media Patent License 1.0 was not distributed with this |
| * source code in the PATENTS file, you can obtain it at |
| * www.aomedia.org/license/patent. |
| */ |
| #include "iamf/cli/parameters_manager.h" |
| |
| #include <cstdint> |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/status/status.h" |
| #include "absl/status/status_matchers.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "iamf/cli/audio_element_with_data.h" |
| #include "iamf/cli/parameter_block_with_data.h" |
| #include "iamf/cli/tests/cli_test_utils.h" |
| #include "iamf/obu/audio_element.h" |
| #include "iamf/obu/codec_config.h" |
| #include "iamf/obu/demixing_info_parameter_data.h" |
| #include "iamf/obu/demixing_param_definition.h" |
| #include "iamf/obu/obu_header.h" |
| #include "iamf/obu/param_definitions.h" |
| #include "iamf/obu/parameter_block.h" |
| #include "iamf/obu/recon_gain_info_parameter_data.h" |
| #include "iamf/obu/types.h" |
| |
| namespace iamf_tools { |
| namespace { |
| |
| using ::absl_testing::IsOk; |
| |
| constexpr DecodedUleb128 kCodecConfigId = 1450; |
| constexpr DecodedUleb128 kSampleRate = 16000; |
| constexpr DecodedUleb128 kAudioElementId = 157; |
| constexpr DecodedUleb128 kFirstSubstreamId = 0; |
| constexpr DecodedUleb128 kSecondSubstreamId = 1; |
| constexpr DecodedUleb128 kParameterId = 995; |
| constexpr DecodedUleb128 kSecondParameterId = 996; |
| constexpr DecodedUleb128 kDuration = 8; |
| constexpr InternalTimestamp kDurationAsInternalTimestamp = 8; |
| |
| constexpr DemixingInfoParameterData::DMixPMode kDMixPMode = |
| DemixingInfoParameterData::kDMixPMode3_n; |
| |
| absl::Status AppendParameterBlock( |
| DecodedUleb128 parameter_id, InternalTimestamp start_timestamp, |
| const ParamDefinition& param_definition, |
| std::vector<ParameterBlockWithData>& parameter_blocks) { |
| parameter_blocks.emplace_back(ParameterBlockWithData{ |
| std::make_unique<ParameterBlockObu>(ObuHeader(), parameter_id, |
| param_definition), |
| start_timestamp, start_timestamp + kDurationAsInternalTimestamp}); |
| ParameterBlockObu& parameter_block_obu = *parameter_blocks.back().obu; |
| absl::Status status = parameter_block_obu.InitializeSubblocks(); |
| return status; |
| } |
| |
| absl::Status AddOneDemixingParameterBlock( |
| const ParamDefinition& param_definition, InternalTimestamp start_timestamp, |
| std::vector<ParameterBlockWithData>& parameter_blocks) { |
| auto status = AppendParameterBlock(kParameterId, start_timestamp, |
| param_definition, parameter_blocks); |
| auto demixing_info_param_data = std::make_unique<DemixingInfoParameterData>(); |
| demixing_info_param_data->dmixp_mode = kDMixPMode; |
| ParameterBlockObu& parameter_block_obu = *parameter_blocks.back().obu; |
| parameter_block_obu.subblocks_[0].param_data = |
| std::move(demixing_info_param_data); |
| |
| return status; |
| } |
| |
| absl::Status AddOneReconGainParameterBlock( |
| const ParamDefinition& param_definition, InternalTimestamp start_timestamp, |
| std::vector<ParameterBlockWithData>& parameter_blocks) { |
| auto status = AppendParameterBlock(kSecondParameterId, start_timestamp, |
| param_definition, parameter_blocks); |
| |
| auto recon_gain_info_parameter_data = |
| std::make_unique<ReconGainInfoParameterData>(); |
| recon_gain_info_parameter_data->recon_gain_elements.emplace_back( |
| ReconGainElement{ |
| .recon_gain_flag = DecodedUleb128(1), |
| .recon_gain = {0}, |
| }); |
| ParameterBlockObu& parameter_block_obu = *parameter_blocks.back().obu; |
| parameter_block_obu.subblocks_[0].param_data = |
| std::move(recon_gain_info_parameter_data); |
| return status; |
| } |
| |
| class ParametersManagerTest : public testing::Test { |
| public: |
| ParametersManagerTest() { |
| AddLpcmCodecConfigWithIdAndSampleRate(kCodecConfigId, kSampleRate, |
| codec_config_obus_); |
| AddAmbisonicsMonoAudioElementWithSubstreamIds( |
| kAudioElementId, kCodecConfigId, {kFirstSubstreamId}, |
| codec_config_obus_, audio_elements_); |
| |
| auto& audio_element_obu = audio_elements_.at(kAudioElementId).obu; |
| AddDemixingParamDefinition(kParameterId, kSampleRate, kDuration, |
| audio_element_obu); |
| |
| EXPECT_THAT( |
| AddOneDemixingParameterBlock( |
| std::get<DemixingParamDefinition>( |
| audio_element_obu.audio_element_params_[0].param_definition), |
| /*start_timestamp=*/0, demixing_parameter_blocks_), |
| IsOk()); |
| } |
| |
| protected: |
| absl::flat_hash_map<uint32_t, CodecConfigObu> codec_config_obus_; |
| absl::flat_hash_map<DecodedUleb128, AudioElementWithData> audio_elements_; |
| std::vector<ParameterBlockWithData> demixing_parameter_blocks_; |
| std::vector<ParameterBlockWithData> recon_gain_parameter_blocks_; |
| std::unique_ptr<ParametersManager> parameters_manager_; |
| }; |
| |
| TEST_F(ParametersManagerTest, InitializeSucceeds) { |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| EXPECT_THAT(parameters_manager_->Initialize(), IsOk()); |
| } |
| |
| TEST_F(ParametersManagerTest, InitializeWithTwoDemixingParametersFails) { |
| // Add one more demixing parameter definition, which is disallowed. |
| AddDemixingParamDefinition(kParameterId, kSampleRate, kDuration, |
| audio_elements_.at(kAudioElementId).obu); |
| |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| EXPECT_FALSE(parameters_manager_->Initialize().ok()); |
| } |
| |
| TEST_F(ParametersManagerTest, InitializeWithReconGainParameterSucceeds) { |
| // Remove existng param definitions added in the constructor of the |
| // test fixture. |
| audio_elements_.at(kAudioElementId).obu.audio_element_params_.clear(); |
| AddReconGainParamDefinition(kSecondParameterId, kSampleRate, kDuration, |
| audio_elements_.at(kAudioElementId).obu); |
| EXPECT_THAT( |
| AddOneReconGainParameterBlock( |
| std::get<ReconGainParamDefinition>(audio_elements_.at(kAudioElementId) |
| .obu.audio_element_params_[0] |
| .param_definition), |
| /*start_timestamp=*/0, recon_gain_parameter_blocks_), |
| IsOk()); |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| EXPECT_THAT(parameters_manager_->Initialize(), IsOk()); |
| } |
| |
| TEST_F(ParametersManagerTest, DemixingParamDefinitionIsAvailable) { |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| ASSERT_THAT(parameters_manager_->Initialize(), IsOk()); |
| |
| EXPECT_TRUE( |
| parameters_manager_->DemixingParamDefinitionAvailable(kAudioElementId)); |
| } |
| |
| TEST_F(ParametersManagerTest, GetDownMixingParametersSucceeds) { |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| ASSERT_THAT(parameters_manager_->Initialize(), IsOk()); |
| parameters_manager_->AddDemixingParameterBlock( |
| &demixing_parameter_blocks_[0]); |
| |
| DownMixingParams down_mixing_params; |
| EXPECT_THAT(parameters_manager_->GetDownMixingParameters(kAudioElementId, |
| down_mixing_params), |
| IsOk()); |
| |
| // Validate the values correspond to `kDMixPMode3_n`. |
| EXPECT_FLOAT_EQ(down_mixing_params.alpha, 1.0); |
| EXPECT_FLOAT_EQ(down_mixing_params.beta, 0.866); |
| EXPECT_FLOAT_EQ(down_mixing_params.gamma, 0.866); |
| EXPECT_FLOAT_EQ(down_mixing_params.delta, 0.866); |
| EXPECT_EQ(down_mixing_params.w_idx_offset, 1); |
| EXPECT_EQ(down_mixing_params.w_idx_used, 0); |
| EXPECT_FLOAT_EQ(down_mixing_params.w, 0.0); |
| } |
| |
| TEST_F(ParametersManagerTest, GetReconGainInfoParameterDataSucceeds) { |
| AddReconGainParamDefinition(kSecondParameterId, kSampleRate, kDuration, |
| audio_elements_.at(kAudioElementId).obu); |
| ASSERT_THAT( |
| AddOneReconGainParameterBlock( |
| std::get<ReconGainParamDefinition>(audio_elements_.at(kAudioElementId) |
| .obu.audio_element_params_[1] |
| .param_definition), |
| /*start_timestamp=*/0, recon_gain_parameter_blocks_), |
| IsOk()); |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| ASSERT_THAT(parameters_manager_->Initialize(), IsOk()); |
| parameters_manager_->AddReconGainParameterBlock( |
| &recon_gain_parameter_blocks_[0]); |
| |
| ReconGainInfoParameterData recon_gain_info_parameter_data; |
| EXPECT_THAT( |
| parameters_manager_->GetReconGainInfoParameterData( |
| kAudioElementId, /*num_layers=*/1, recon_gain_info_parameter_data), |
| IsOk()); |
| |
| EXPECT_EQ(recon_gain_info_parameter_data.recon_gain_elements.size(), 1); |
| ASSERT_TRUE( |
| recon_gain_info_parameter_data.recon_gain_elements[0].has_value()); |
| EXPECT_EQ( |
| recon_gain_info_parameter_data.recon_gain_elements[0]->recon_gain_flag, |
| DecodedUleb128(1)); |
| EXPECT_EQ( |
| recon_gain_info_parameter_data.recon_gain_elements[0]->recon_gain[0], 0); |
| } |
| |
| TEST_F(ParametersManagerTest, |
| GetReconGainInfoParameterDataSucceedsWithNoParameterBlocks) { |
| AddReconGainParamDefinition(kSecondParameterId, kSampleRate, kDuration, |
| audio_elements_.at(kAudioElementId).obu); |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| ASSERT_THAT(parameters_manager_->Initialize(), IsOk()); |
| |
| ReconGainInfoParameterData recon_gain_info_parameter_data; |
| EXPECT_THAT( |
| parameters_manager_->GetReconGainInfoParameterData( |
| kAudioElementId, /*num_layers=*/1, recon_gain_info_parameter_data), |
| IsOk()); |
| |
| EXPECT_EQ(recon_gain_info_parameter_data.recon_gain_elements.size(), 1); |
| ASSERT_TRUE( |
| recon_gain_info_parameter_data.recon_gain_elements[0].has_value()); |
| EXPECT_EQ( |
| recon_gain_info_parameter_data.recon_gain_elements[0]->recon_gain_flag, |
| DecodedUleb128(0)); |
| EXPECT_EQ( |
| recon_gain_info_parameter_data.recon_gain_elements[0]->recon_gain[0], |
| 255); |
| } |
| |
| TEST_F(ParametersManagerTest, |
| GetReconGainInfoParameterDataSucceedsWithNoParamDefinition) { |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| ASSERT_THAT(parameters_manager_->Initialize(), IsOk()); |
| |
| ReconGainInfoParameterData recon_gain_info_parameter_data; |
| EXPECT_THAT( |
| parameters_manager_->GetReconGainInfoParameterData( |
| kAudioElementId, /*num_layers=*/1, recon_gain_info_parameter_data), |
| IsOk()); |
| |
| EXPECT_EQ(recon_gain_info_parameter_data.recon_gain_elements.size(), 1); |
| ASSERT_TRUE( |
| recon_gain_info_parameter_data.recon_gain_elements[0].has_value()); |
| EXPECT_EQ( |
| recon_gain_info_parameter_data.recon_gain_elements[0]->recon_gain_flag, |
| DecodedUleb128(0)); |
| EXPECT_EQ( |
| recon_gain_info_parameter_data.recon_gain_elements[0]->recon_gain[0], |
| 255); |
| } |
| |
| TEST_F(ParametersManagerTest, GetMultipleReconGainParametersSucceeds) { |
| // Tests that multiple recon gain parameters are returned correctly when there |
| // are multiple recon gain parameter blocks within the same substream, with |
| // consecutive timestamps. |
| AddReconGainParamDefinition(kSecondParameterId, kSampleRate, kDuration, |
| audio_elements_.at(kAudioElementId).obu); |
| ASSERT_THAT( |
| AddOneReconGainParameterBlock( |
| std::get<ReconGainParamDefinition>(audio_elements_.at(kAudioElementId) |
| .obu.audio_element_params_[1] |
| .param_definition), |
| /*start_timestamp=*/0, recon_gain_parameter_blocks_), |
| IsOk()); |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| ASSERT_THAT(parameters_manager_->Initialize(), IsOk()); |
| parameters_manager_->AddReconGainParameterBlock( |
| &recon_gain_parameter_blocks_[0]); |
| |
| // First recon gain parameter block. |
| ReconGainInfoParameterData recon_gain_parameter_data_0; |
| EXPECT_THAT( |
| parameters_manager_->GetReconGainInfoParameterData( |
| kAudioElementId, /*num_layers=*/1, recon_gain_parameter_data_0), |
| IsOk()); |
| EXPECT_EQ(recon_gain_parameter_data_0.recon_gain_elements.size(), 1); |
| ASSERT_TRUE(recon_gain_parameter_data_0.recon_gain_elements[0].has_value()); |
| EXPECT_EQ(recon_gain_parameter_data_0.recon_gain_elements[0]->recon_gain_flag, |
| DecodedUleb128(1)); |
| EXPECT_EQ(recon_gain_parameter_data_0.recon_gain_elements[0]->recon_gain[0], |
| 0); |
| |
| EXPECT_THAT(parameters_manager_->UpdateReconGainState( |
| kAudioElementId, |
| /*expected_next_timestamp=*/kDuration), |
| IsOk()); |
| |
| // Second recon gain parameter block. |
| ASSERT_THAT( |
| AddOneReconGainParameterBlock( |
| std::get<ReconGainParamDefinition>(audio_elements_.at(kAudioElementId) |
| .obu.audio_element_params_[1] |
| .param_definition), |
| /*start_timestamp=*/kDurationAsInternalTimestamp, |
| recon_gain_parameter_blocks_), |
| IsOk()); |
| parameters_manager_->AddReconGainParameterBlock( |
| &recon_gain_parameter_blocks_[1]); |
| ReconGainInfoParameterData recon_gain_parameter_data_1; |
| EXPECT_THAT( |
| parameters_manager_->GetReconGainInfoParameterData( |
| kAudioElementId, /*num_layers=*/1, recon_gain_parameter_data_1), |
| IsOk()); |
| EXPECT_EQ(recon_gain_parameter_data_1.recon_gain_elements.size(), 1); |
| ASSERT_TRUE(recon_gain_parameter_data_1.recon_gain_elements[0].has_value()); |
| EXPECT_EQ(recon_gain_parameter_data_1.recon_gain_elements[0]->recon_gain_flag, |
| DecodedUleb128(1)); |
| EXPECT_EQ(recon_gain_parameter_data_1.recon_gain_elements[0]->recon_gain[0], |
| 0); |
| // Updating should succeed a second time with the expected timestamp now |
| // offset by the duration of the parameter block. |
| EXPECT_THAT(parameters_manager_->UpdateReconGainState( |
| kAudioElementId, |
| /*expected_next_timestamp=*/kDuration + kDuration), |
| IsOk()); |
| } |
| |
| TEST_F(ParametersManagerTest, |
| GetMultipleReconGainParametersFailsWithoutUpdatingState) { |
| AddReconGainParamDefinition(kSecondParameterId, kSampleRate, kDuration, |
| audio_elements_.at(kAudioElementId).obu); |
| ASSERT_THAT( |
| AddOneReconGainParameterBlock( |
| std::get<ReconGainParamDefinition>(audio_elements_.at(kAudioElementId) |
| .obu.audio_element_params_[1] |
| .param_definition), |
| /*start_timestamp=*/0, recon_gain_parameter_blocks_), |
| IsOk()); |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| ASSERT_THAT(parameters_manager_->Initialize(), IsOk()); |
| parameters_manager_->AddReconGainParameterBlock( |
| &recon_gain_parameter_blocks_[0]); |
| |
| // First recon gain parameter block. |
| ReconGainInfoParameterData recon_gain_parameter_data_0; |
| EXPECT_THAT( |
| parameters_manager_->GetReconGainInfoParameterData( |
| kAudioElementId, /*num_layers=*/1, recon_gain_parameter_data_0), |
| IsOk()); |
| |
| // Second recon gain parameter block. |
| ASSERT_THAT( |
| AddOneReconGainParameterBlock( |
| std::get<ReconGainParamDefinition>(audio_elements_.at(kAudioElementId) |
| .obu.audio_element_params_[1] |
| .param_definition), |
| /*start_timestamp=*/kDurationAsInternalTimestamp, |
| recon_gain_parameter_blocks_), |
| IsOk()); |
| parameters_manager_->AddReconGainParameterBlock( |
| &recon_gain_parameter_blocks_[1]); |
| ReconGainInfoParameterData recon_gain_parameter_data_1; |
| EXPECT_FALSE(parameters_manager_ |
| ->GetReconGainInfoParameterData(kAudioElementId, |
| /*num_layers=*/1, |
| recon_gain_parameter_data_1) |
| .ok()); |
| } |
| |
| TEST_F(ParametersManagerTest, ParameterBlocksRunOutReturnsDefault) { |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| ASSERT_THAT(parameters_manager_->Initialize(), IsOk()); |
| |
| parameters_manager_->AddDemixingParameterBlock( |
| &demixing_parameter_blocks_[0]); |
| |
| DownMixingParams down_mixing_params; |
| EXPECT_THAT(parameters_manager_->GetDownMixingParameters(kAudioElementId, |
| down_mixing_params), |
| IsOk()); |
| |
| EXPECT_THAT(parameters_manager_->UpdateDemixingState( |
| kAudioElementId, |
| /*expected_next_timestamp=*/kDurationAsInternalTimestamp), |
| IsOk()); |
| |
| // Get the parameters for the second time. Since there is only one |
| // parameter block and is already used up the previous time, the function |
| // will not find a parameter block and will return default values. |
| EXPECT_THAT(parameters_manager_->GetDownMixingParameters(kAudioElementId, |
| down_mixing_params), |
| IsOk()); |
| |
| // Validate the values correspond to `kDMixPMode1` and `default_w = 10`, |
| // which are the default set in `AddDemixingParamDefinition()`. |
| EXPECT_FLOAT_EQ(down_mixing_params.alpha, 1.0); |
| EXPECT_FLOAT_EQ(down_mixing_params.beta, 1.0); |
| EXPECT_FLOAT_EQ(down_mixing_params.gamma, 0.707); |
| EXPECT_FLOAT_EQ(down_mixing_params.delta, 0.707); |
| EXPECT_EQ(down_mixing_params.w_idx_offset, -1); |
| EXPECT_EQ(down_mixing_params.w_idx_used, 10); |
| EXPECT_FLOAT_EQ(down_mixing_params.w, 0.5); |
| |
| // `UpdateDemixingState()` also succeeds with some arbitrary timestamp, |
| // because technically there's nothing to update. |
| const DecodedUleb128 kArbitraryTimestamp = 972; |
| EXPECT_THAT(parameters_manager_->UpdateDemixingState( |
| kAudioElementId, |
| /*expected_next_timestamp=*/kArbitraryTimestamp), |
| IsOk()); |
| } |
| |
| TEST_F(ParametersManagerTest, ParameterIdNotFoundReturnsDefault) { |
| // Modify the parameter definition of the audio element so it does not |
| // correspond to any parameter blocks inside `parameter_blocks_`. |
| std::get<DemixingParamDefinition>(audio_elements_.at(kAudioElementId) |
| .obu.audio_element_params_[0] |
| .param_definition) |
| .parameter_id_ = kParameterId + 1; |
| |
| // Create the parameters manager and get down mixing parameters; default |
| // values are returned because the parameter ID is different from those |
| // in the `parameter_blocks_`. |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| ASSERT_THAT(parameters_manager_->Initialize(), IsOk()); |
| parameters_manager_->AddDemixingParameterBlock( |
| &demixing_parameter_blocks_[0]); |
| |
| DownMixingParams down_mixing_params; |
| EXPECT_THAT(parameters_manager_->GetDownMixingParameters(kAudioElementId, |
| down_mixing_params), |
| IsOk()); |
| |
| // Validate the values correspond to `kDMixPMode1` and `default_w = 10`, |
| // which are the default set in `AddDemixingParamDefinition()`. |
| EXPECT_FLOAT_EQ(down_mixing_params.alpha, 1.0); |
| EXPECT_FLOAT_EQ(down_mixing_params.beta, 1.0); |
| EXPECT_FLOAT_EQ(down_mixing_params.gamma, 0.707); |
| EXPECT_FLOAT_EQ(down_mixing_params.delta, 0.707); |
| EXPECT_EQ(down_mixing_params.w_idx_offset, -1); |
| EXPECT_EQ(down_mixing_params.w_idx_used, 10); |
| EXPECT_FLOAT_EQ(down_mixing_params.w, 0.5); |
| } |
| |
| TEST_F(ParametersManagerTest, GetDownMixingParametersTwiceDifferentW) { |
| // Add another parameter block, so we can get down-mix parameters twice. |
| ASSERT_THAT( |
| AddOneDemixingParameterBlock( |
| std::get<DemixingParamDefinition>(audio_elements_.at(kAudioElementId) |
| .obu.audio_element_params_[0] |
| .param_definition), |
| /*start_timestamp=*/kDuration, demixing_parameter_blocks_), |
| IsOk()); |
| |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| ASSERT_THAT(parameters_manager_->Initialize(), IsOk()); |
| parameters_manager_->AddDemixingParameterBlock( |
| &demixing_parameter_blocks_[0]); |
| |
| // Get down-mix parameters for the first time. |
| DownMixingParams down_mixing_params; |
| ASSERT_THAT(parameters_manager_->GetDownMixingParameters(kAudioElementId, |
| down_mixing_params), |
| IsOk()); |
| EXPECT_THAT(parameters_manager_->UpdateDemixingState( |
| kAudioElementId, |
| /*expected_next_timestamp=*/kDuration), |
| IsOk()); |
| |
| // The first time `w_idx` is 0, and the corresponding `w` is 0. |
| const double kWFirst = 0.0; |
| const double kWSecond = 0.0179; |
| EXPECT_FLOAT_EQ(down_mixing_params.w, kWFirst); |
| |
| // Add and get down-mix parameters for the second time. |
| parameters_manager_->AddDemixingParameterBlock( |
| &demixing_parameter_blocks_[1]); |
| EXPECT_THAT(parameters_manager_->GetDownMixingParameters(kAudioElementId, |
| down_mixing_params), |
| IsOk()); |
| |
| // Validate the values correspond to `kDMixPMode3_n`. Since `w_idx` has |
| // been updated to 1, `w` becomes 0.0179. |
| EXPECT_FLOAT_EQ(down_mixing_params.alpha, 1.0); |
| EXPECT_FLOAT_EQ(down_mixing_params.beta, 0.866); |
| EXPECT_FLOAT_EQ(down_mixing_params.gamma, 0.866); |
| EXPECT_FLOAT_EQ(down_mixing_params.delta, 0.866); |
| EXPECT_EQ(down_mixing_params.w_idx_offset, 1); |
| EXPECT_EQ(down_mixing_params.w_idx_used, 1); |
| |
| // Updated `w`, different from the first time above. |
| EXPECT_FLOAT_EQ(down_mixing_params.w, kWSecond); |
| } |
| |
| TEST_F(ParametersManagerTest, GetDownMixingParametersTwiceWithoutUpdateSameW) { |
| // Add another parameter block, so it is possible to get down-mix parameters |
| // twice. |
| ASSERT_THAT( |
| AddOneDemixingParameterBlock( |
| std::get<DemixingParamDefinition>(audio_elements_.at(kAudioElementId) |
| .obu.audio_element_params_[0] |
| .param_definition), |
| /*start_timestamp=*/kDuration, demixing_parameter_blocks_), |
| IsOk()); |
| |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| ASSERT_THAT(parameters_manager_->Initialize(), IsOk()); |
| parameters_manager_->AddDemixingParameterBlock( |
| &demixing_parameter_blocks_[0]); |
| |
| // Get down-mix parameters twice without calling |
| // `AddDemixingParameterBlock()` and `UpdateDemixngState()`; the same |
| // down-mix parameters will be returned. |
| DownMixingParams down_mixing_params; |
| ASSERT_THAT(parameters_manager_->GetDownMixingParameters(kAudioElementId, |
| down_mixing_params), |
| IsOk()); |
| |
| // The first time `w_idx` is 0, and the corresponding `w` is 0. |
| EXPECT_EQ(down_mixing_params.w_idx_used, 0); |
| EXPECT_FLOAT_EQ(down_mixing_params.w, 0.0); |
| |
| EXPECT_THAT(parameters_manager_->GetDownMixingParameters(kAudioElementId, |
| down_mixing_params), |
| IsOk()); |
| |
| // Validate the values correspond to `kDMixPMode3_n`. Since `w_idx` has |
| // NOT been updated, `w` remains 0.0. |
| EXPECT_FLOAT_EQ(down_mixing_params.alpha, 1.0); |
| EXPECT_FLOAT_EQ(down_mixing_params.beta, 0.866); |
| EXPECT_FLOAT_EQ(down_mixing_params.gamma, 0.866); |
| EXPECT_FLOAT_EQ(down_mixing_params.delta, 0.866); |
| EXPECT_EQ(down_mixing_params.w_idx_offset, 1); |
| EXPECT_EQ(down_mixing_params.w_idx_used, 0); |
| EXPECT_FLOAT_EQ(down_mixing_params.w, 0.0); |
| } |
| |
| TEST_F(ParametersManagerTest, |
| TwoAudioElementGettingParameterBlocksWithDifferentTimestampsFails) { |
| // Add another parameter block, so we can get down-mix parameters twice. |
| ASSERT_THAT( |
| AddOneDemixingParameterBlock( |
| std::get<DemixingParamDefinition>(audio_elements_.at(kAudioElementId) |
| .obu.audio_element_params_[0] |
| .param_definition), |
| /*start_timestamp=*/kDuration, demixing_parameter_blocks_), |
| IsOk()); |
| |
| // Add a second audio element sharing the same demixing parameter. |
| constexpr DecodedUleb128 kAudioElementId2 = kAudioElementId + 1; |
| AddAmbisonicsMonoAudioElementWithSubstreamIds( |
| kAudioElementId2, kCodecConfigId, {kSecondSubstreamId}, |
| codec_config_obus_, audio_elements_); |
| auto& second_audio_element_obu = audio_elements_.at(kAudioElementId2).obu; |
| AddDemixingParamDefinition(kParameterId, kSampleRate, kDuration, |
| second_audio_element_obu); |
| |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| ASSERT_THAT(parameters_manager_->Initialize(), IsOk()); |
| parameters_manager_->AddDemixingParameterBlock( |
| &demixing_parameter_blocks_[0]); |
| |
| // Get down-mix parameters for the first audio element corresponding to the |
| // first frame; the `w` value is 0. |
| const double kWFirst = 0.0; |
| const double kWSecond = 0.0179; |
| DownMixingParams down_mixing_params; |
| ASSERT_THAT(parameters_manager_->GetDownMixingParameters(kAudioElementId, |
| down_mixing_params), |
| IsOk()); |
| EXPECT_THAT(parameters_manager_->UpdateDemixingState( |
| kAudioElementId, |
| /*expected_next_timestamp=*/kDuration), |
| IsOk()); |
| EXPECT_FLOAT_EQ(down_mixing_params.w, kWFirst); |
| |
| // Add the parameter block for the first audio element corresponding to the |
| // second frame. |
| parameters_manager_->AddDemixingParameterBlock( |
| &demixing_parameter_blocks_[1]); |
| ASSERT_THAT(parameters_manager_->GetDownMixingParameters(kAudioElementId, |
| down_mixing_params), |
| IsOk()); |
| EXPECT_FLOAT_EQ(down_mixing_params.w, kWSecond); |
| |
| // Get down-mix parameters for the second audio element. The second audio |
| // element shares the same parameter ID, but is still expecting the |
| // parameter block for the first frame (while the manager is already |
| // holding the parameter block for the second frame). So the getter fails. |
| EXPECT_FALSE( |
| parameters_manager_ |
| ->GetDownMixingParameters(kAudioElementId2, down_mixing_params) |
| .ok()); |
| } |
| |
| TEST_F(ParametersManagerTest, DemixingParamDefinitionIsNotAvailableForWrongId) { |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| ASSERT_THAT(parameters_manager_->Initialize(), IsOk()); |
| parameters_manager_->AddDemixingParameterBlock( |
| &demixing_parameter_blocks_[0]); |
| |
| const DecodedUleb128 kWrongAudioElementId = kAudioElementId + 1; |
| EXPECT_FALSE(parameters_manager_->DemixingParamDefinitionAvailable( |
| kWrongAudioElementId)); |
| |
| // However, `GetDownMixingParameters()` still succeeds. |
| DownMixingParams down_mixing_params; |
| EXPECT_THAT(parameters_manager_->GetDownMixingParameters(kWrongAudioElementId, |
| down_mixing_params), |
| IsOk()); |
| |
| // `UpdateDemixingState()` also succeeds. |
| EXPECT_THAT(parameters_manager_->UpdateDemixingState( |
| kWrongAudioElementId, |
| /*expected_next_timestamp=*/kDuration), |
| IsOk()); |
| } |
| |
| TEST_F(ParametersManagerTest, UpdateFailsWithWrongTimestamps) { |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| ASSERT_THAT(parameters_manager_->Initialize(), IsOk()); |
| parameters_manager_->AddDemixingParameterBlock( |
| &demixing_parameter_blocks_[0]); |
| |
| // The second frame starts with timestamp = 8, so updating with a different |
| // timestamp fails. |
| constexpr InternalTimestamp kWrongNextTimestamp = 17; |
| EXPECT_FALSE(parameters_manager_ |
| ->UpdateDemixingState(kAudioElementId, kWrongNextTimestamp) |
| .ok()); |
| } |
| |
| TEST_F(ParametersManagerTest, UpdateNotValidatingWhenParameterIdNotFound) { |
| // Modify the parameter definition of the audio element so it does not |
| // correspond to any parameter blocks inside `parameter_blocks_`. |
| std::get<DemixingParamDefinition>(audio_elements_.at(kAudioElementId) |
| .obu.audio_element_params_[0] |
| .param_definition) |
| .parameter_id_ = kParameterId + 1; |
| |
| // Create the parameters manager and get down mixing parameters; default |
| // values are returned because the parameter ID is not found. |
| parameters_manager_ = std::make_unique<ParametersManager>(audio_elements_); |
| ASSERT_THAT(parameters_manager_->Initialize(), IsOk()); |
| parameters_manager_->AddDemixingParameterBlock( |
| &demixing_parameter_blocks_[0]); |
| |
| // `UpdateDemixingState()` succeeds with any timestamp passed in, |
| // because no validation is performed. |
| for (const InternalTimestamp timestamp : {0, 8, -200, 61, 4772}) { |
| EXPECT_THAT( |
| parameters_manager_->UpdateDemixingState(kAudioElementId, timestamp), |
| IsOk()); |
| } |
| } |
| |
| } // namespace |
| } // namespace iamf_tools |