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