• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024, Alliance for Open Media. All rights reserved
3  *
4  * This source code is subject to the terms of the BSD 3-Clause Clear License
5  * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
6  * License was not distributed with this source code in the LICENSE file, you
7  * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
8  * Alliance for Open Media Patent License 1.0 was not distributed with this
9  * source code in the PATENTS file, you can obtain it at
10  * www.aomedia.org/license/patent.
11  */
12 
13 #include "iamf/cli/renderer/audio_element_renderer_ambisonics_to_channel.h"
14 
15 #include <cstddef>
16 #include <cstdint>
17 #include <memory>
18 #include <string>
19 #include <vector>
20 
21 #include "absl/log/log.h"
22 #include "absl/memory/memory.h"
23 #include "absl/status/status.h"
24 #include "absl/strings/str_cat.h"
25 #include "absl/types/span.h"
26 #include "iamf/cli/audio_element_with_data.h"
27 #include "iamf/cli/channel_label.h"
28 #include "iamf/cli/renderer/loudspeakers_renderer.h"
29 #include "iamf/cli/renderer/renderer_utils.h"
30 #include "iamf/common/utils/macros.h"
31 #include "iamf/obu/audio_element.h"
32 #include "iamf/obu/mix_presentation.h"
33 #include "iamf/obu/types.h"
34 namespace iamf_tools {
35 namespace {
36 
GetAmbisonicsOrder(const uint8_t channel_count,int & order)37 absl::Status GetAmbisonicsOrder(const uint8_t channel_count, int& order) {
38   // IAMF section 3.6.3 (https://aomediacodec.github.io/iamf/#obu-codecconfig)
39   // only permits ambisonics orders [0, 14].
40   static const int kMaxAmbisonicsOrder = 14;
41   for (int i = 0; i < kMaxAmbisonicsOrder; i++) {
42     const int expected_count = (i + 1) * (i + 1);
43     if (static_cast<int>(channel_count) == expected_count) {
44       order = i;
45       return absl::OkStatus();
46     } else if (static_cast<int>(channel_count) < expected_count) {
47       // Set to upper bound when `IGNORE_ERRORS_USE_ONLY_FOR_IAMF_TEST_SUITE` is
48       // defined.
49       order = i;
50       break;
51     }
52   }
53 
54   return absl::UnknownError(absl::StrCat(
55       channel_count, " is not a valid number of ambisonics channels."));
56 }
57 
InitializeChannelLabelsSceneBased(const AmbisonicsConfig & ambisonics_config,const std::vector<DecodedUleb128> & audio_substream_ids,const SubstreamIdLabelsMap & substream_id_to_labels,std::vector<ChannelLabel::Label> & channel_labels)58 absl::Status InitializeChannelLabelsSceneBased(
59     const AmbisonicsConfig& ambisonics_config,
60     const std::vector<DecodedUleb128>& audio_substream_ids,
61     const SubstreamIdLabelsMap& substream_id_to_labels,
62     std::vector<ChannelLabel::Label>& channel_labels) {
63   switch (ambisonics_config.ambisonics_mode) {
64     using enum AmbisonicsConfig::AmbisonicsMode;
65     case kAmbisonicsModeMono: {
66       const auto& ambisonics_mono_config =
67           std::get<AmbisonicsMonoConfig>(ambisonics_config.ambisonics_config);
68       RETURN_IF_NOT_OK(
69           ambisonics_mono_config.Validate(audio_substream_ids.size()));
70       channel_labels.resize(ambisonics_mono_config.output_channel_count);
71       for (int i = 0; i < channel_labels.size(); ++i) {
72         const uint8_t substream_id_index =
73             ambisonics_mono_config.channel_mapping[i];
74         if (substream_id_index ==
75             AmbisonicsMonoConfig::kInactiveAmbisonicsChannelNumber) {
76           // Mixed-order ambisonics representation: this channel is missing.
77           channel_labels[i] = ChannelLabel::kOmitted;
78         } else {
79           const auto substream_id = audio_substream_ids[substream_id_index];
80           const auto& labels = substream_id_to_labels.at(substream_id);
81 
82           // For ambisonics mode = MONO, each substream should correspond to
83           // only one channel.
84           if (labels.size() != 1) {
85             return absl::InvalidArgumentError(absl::StrCat(
86                 "Expected one channel per substream for `kAmbisonicsModeMono`. "
87                 "substream_id= ",
88                 substream_id, " contains ", labels.size(), " channels."));
89           }
90           channel_labels[i] = labels.front();
91         }
92       }
93 
94       break;
95     }
96     case kAmbisonicsModeProjection: {
97       const auto& ambisonics_projection_config =
98           std::get<AmbisonicsProjectionConfig>(
99               ambisonics_config.ambisonics_config);
100       RETURN_IF_NOT_OK(
101           ambisonics_projection_config.Validate(audio_substream_ids.size()));
102       const int num_channels =
103           ambisonics_projection_config.substream_count +
104           ambisonics_projection_config.coupled_substream_count;
105       channel_labels.reserve(num_channels);
106       for (int i = 0; i < ambisonics_projection_config.substream_count; i++) {
107         const auto substream_id = audio_substream_ids[i];
108         for (const auto& label : substream_id_to_labels.at(substream_id)) {
109           channel_labels.push_back(label);
110         }
111       }
112 
113       if (channel_labels.size() != num_channels) {
114         return absl::InvalidArgumentError(absl::StrCat(
115             "Inconsistent number of channels. channel_labels->size()= ",
116             channel_labels.size(), " vs num_channels= ", num_channels));
117       }
118 
119       break;
120     }
121     default:
122       return absl::UnimplementedError(absl::StrCat(
123           "Unsupported ambisonics_mode= ", ambisonics_config.ambisonics_mode));
124   }
125 
126   return absl::OkStatus();
127 }
128 
129 }  // namespace
130 
131 std::unique_ptr<AudioElementRendererAmbisonicsToChannel>
CreateFromAmbisonicsConfig(const AmbisonicsConfig & ambisonics_config,const std::vector<DecodedUleb128> & audio_substream_ids,const SubstreamIdLabelsMap & substream_id_to_labels,const Layout & playback_layout,size_t num_samples_per_frame)132 AudioElementRendererAmbisonicsToChannel::CreateFromAmbisonicsConfig(
133     const AmbisonicsConfig& ambisonics_config,
134     const std::vector<DecodedUleb128>& audio_substream_ids,
135     const SubstreamIdLabelsMap& substream_id_to_labels,
136     const Layout& playback_layout, size_t num_samples_per_frame) {
137   // Exclude unsupported modes first, and deal with only mono or projection
138   // in the rest of the code.
139   const auto mode = ambisonics_config.ambisonics_mode;
140   if (mode != AmbisonicsConfig::kAmbisonicsModeMono &&
141       mode != AmbisonicsConfig::kAmbisonicsModeProjection) {
142     LOG(ERROR) << "Unsupported ambisonics mode. mode= " << mode;
143     return nullptr;
144   }
145   const bool is_mono = mode == AmbisonicsConfig::kAmbisonicsModeMono;
146 
147   // Input key for ambisonics is "A{ambisonics_order}".
148   const int output_channel_count =
149       is_mono
150           ? std::get<AmbisonicsMonoConfig>(ambisonics_config.ambisonics_config)
151                 .output_channel_count
152           : std::get<AmbisonicsProjectionConfig>(
153                 ambisonics_config.ambisonics_config)
154                 .output_channel_count;
155   std::vector<ChannelLabel::Label> channel_labels;
156   if (const auto status = InitializeChannelLabelsSceneBased(
157           ambisonics_config, audio_substream_ids, substream_id_to_labels,
158           channel_labels);
159       !status.ok()) {
160     LOG(ERROR) << status;
161     return nullptr;
162   }
163 
164   const auto& output_key =
165       renderer_utils::LookupOutputKeyFromPlaybackLayout(playback_layout);
166   if (!output_key.ok()) {
167     LOG(ERROR) << output_key.status();
168     return nullptr;
169   }
170 
171   int ambisonics_order = 0;
172   if (const auto status =
173           GetAmbisonicsOrder(output_channel_count, ambisonics_order);
174       !status.ok()) {
175     LOG(ERROR) << status;
176     return nullptr;
177   }
178   const std::string input_key = absl::StrCat("A", ambisonics_order);
179   const auto& gains = LookupPrecomputedGains(input_key, *output_key);
180   if (!gains.ok()) {
181     LOG(ERROR) << gains.status();
182     return nullptr;
183   }
184   const std::string gains_map_key =
185       absl::StrCat("A", ambisonics_order, "->", *output_key);
186 
187   int32_t num_output_channels = 0;
188   if (!MixPresentationObu::GetNumChannelsFromLayout(playback_layout,
189                                                     num_output_channels)
190            .ok()) {
191     return nullptr;
192   }
193 
194   return absl::WrapUnique(new AudioElementRendererAmbisonicsToChannel(
195       static_cast<size_t>(num_output_channels), num_samples_per_frame,
196       ambisonics_config, channel_labels, *gains));
197 }
198 
RenderSamples(absl::Span<const std::vector<InternalSampleType>> samples_to_render,std::vector<InternalSampleType> & rendered_samples)199 absl::Status AudioElementRendererAmbisonicsToChannel::RenderSamples(
200     absl::Span<const std::vector<InternalSampleType>> samples_to_render,
201     std::vector<InternalSampleType>& rendered_samples) {
202   // Render the samples.
203   RETURN_IF_NOT_OK(RenderAmbisonicsToLoudspeakers(
204       samples_to_render, ambisonics_config_, gains_, rendered_samples));
205 
206   return absl::OkStatus();
207 }
208 
209 }  // namespace iamf_tools
210