1
2 /*
3 * Copyright (c) 2024, Alliance for Open Media. All rights reserved
4 *
5 * This source code is subject to the terms of the BSD 3-Clause Clear License
6 * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
7 * License was not distributed with this source code in the LICENSE file, you
8 * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
9 * Alliance for Open Media Patent License 1.0 was not distributed with this
10 * source code in the PATENTS file, you can obtain it at
11 * www.aomedia.org/license/patent.
12 */
13
14 #include "iamf/cli/renderer/audio_element_renderer_channel_to_channel.h"
15
16 #include <cstddef>
17 #include <cstdint>
18 #include <memory>
19 #include <optional>
20 #include <vector>
21
22 #include "absl/base/no_destructor.h"
23 #include "absl/container/flat_hash_map.h"
24 #include "absl/log/log.h"
25 #include "absl/memory/memory.h"
26 #include "absl/status/status.h"
27 #include "absl/strings/str_cat.h"
28 #include "absl/strings/string_view.h"
29 #include "absl/types/span.h"
30 #include "iamf/cli/channel_label.h"
31 #include "iamf/cli/renderer/loudspeakers_renderer.h"
32 #include "iamf/cli/renderer/renderer_utils.h"
33 #include "iamf/common/utils/macros.h"
34 #include "iamf/common/utils/map_utils.h"
35 #include "iamf/common/utils/validation_utils.h"
36 #include "iamf/obu/audio_element.h"
37 #include "iamf/obu/mix_presentation.h"
38 #include "iamf/obu/types.h"
39
40 namespace iamf_tools {
41 namespace {
42
43 constexpr absl::string_view kMonoInputKey = "0+1+0";
44 constexpr absl::string_view kStereoInputKey = "0+2+0";
45 constexpr absl::string_view k5_1_chInputKey = "0+5+0";
46 constexpr absl::string_view k5_1_2_chInputKey = "2+5+0";
47 constexpr absl::string_view k5_1_4InputKey = "4+5+0";
48 constexpr absl::string_view k7_1_0InputKey = "0+7+0";
49 constexpr absl::string_view k7_1_4InputKey = "4+7+0";
50 constexpr absl::string_view k7_1_2InputKey = "7.1.2";
51 constexpr absl::string_view k3_1_2InputKey = "3.1.2";
52 constexpr absl::string_view k9_1_6InputKey = "9.1.6";
53
54 // TODO(b/359180486): Unify with `IsExpandedLayoutEquivalentToSoundSystem` in
55 // `audio_element_passthrough.cc`.
LookupInputKeyFromLoudspeakerLayout(ChannelAudioLayerConfig::ExpandedLoudspeakerLayout expanded_layout)56 absl::StatusOr<absl::string_view> LookupInputKeyFromLoudspeakerLayout(
57 ChannelAudioLayerConfig::ExpandedLoudspeakerLayout expanded_layout) {
58 switch (expanded_layout) {
59 using enum ChannelAudioLayerConfig::ExpandedLoudspeakerLayout;
60 using enum ChannelAudioLayerConfig::LoudspeakerLayout;
61 case kExpandedLayoutStereoS:
62 return k5_1_4InputKey;
63 case kExpandedLayoutLFE:
64 case kExpandedLayoutStereoSS:
65 case kExpandedLayoutStereoRS:
66 case kExpandedLayoutStereoTF:
67 case kExpandedLayoutStereoTB:
68 case kExpandedLayoutTop4Ch:
69 case kExpandedLayout3_0_ch:
70 return k7_1_4InputKey;
71 case kExpandedLayout9_1_6_ch:
72 case kExpandedLayoutStereoF:
73 case kExpandedLayoutStereoSi:
74 case kExpandedLayoutStereoTpSi:
75 case kExpandedLayoutTop6Ch:
76 return k9_1_6InputKey;
77 default:
78 return absl::InvalidArgumentError(absl::StrCat(
79 "Channel order not found for layout= ", expanded_layout));
80 }
81 }
82
LookupInputKeyFromLoudspeakerLayout(ChannelAudioLayerConfig::LoudspeakerLayout loudspeaker_layout,std::optional<ChannelAudioLayerConfig::ExpandedLoudspeakerLayout> expanded_loudspeaker_layout)83 absl::StatusOr<absl::string_view> LookupInputKeyFromLoudspeakerLayout(
84 ChannelAudioLayerConfig::LoudspeakerLayout loudspeaker_layout,
85 std::optional<ChannelAudioLayerConfig::ExpandedLoudspeakerLayout>
86 expanded_loudspeaker_layout) {
87 if (loudspeaker_layout == ChannelAudioLayerConfig::kLayoutExpanded) {
88 RETURN_IF_NOT_OK(ValidateHasValue(expanded_loudspeaker_layout,
89 "expanded_loudspeaker_layout"));
90 return LookupInputKeyFromLoudspeakerLayout(*expanded_loudspeaker_layout);
91 }
92
93 using enum LoudspeakersSsConventionLayout::SoundSystem;
94 using enum ChannelAudioLayerConfig::LoudspeakerLayout;
95
96 static const absl::NoDestructor<absl::flat_hash_map<
97 ChannelAudioLayerConfig::LoudspeakerLayout, absl::string_view>>
98 kLoudspeakerLayoutToInputKey({
99 {kLayoutMono, kMonoInputKey},
100 {kLayoutStereo, kStereoInputKey},
101 {kLayout5_1_ch, k5_1_chInputKey},
102 {kLayout5_1_2_ch, k5_1_2_chInputKey},
103 {kLayout5_1_4_ch, k5_1_4InputKey},
104 {kLayout7_1_ch, k7_1_0InputKey},
105 {kLayout7_1_4_ch, k7_1_4InputKey},
106 {kLayout7_1_2_ch, k7_1_2InputKey},
107 {kLayout3_1_2_ch, k3_1_2InputKey},
108 });
109
110 return LookupInMap(*kLoudspeakerLayoutToInputKey, loudspeaker_layout,
111 "Input key for `LoudspeakerLayout`");
112 }
113
114 } // namespace
115
116 std::unique_ptr<AudioElementRendererChannelToChannel>
CreateFromScalableChannelLayoutConfig(const ScalableChannelLayoutConfig & scalable_channel_layout_config,const Layout & playback_layout,size_t num_samples_per_frame)117 AudioElementRendererChannelToChannel::CreateFromScalableChannelLayoutConfig(
118 const ScalableChannelLayoutConfig& scalable_channel_layout_config,
119 const Layout& playback_layout, size_t num_samples_per_frame) {
120 if (scalable_channel_layout_config.channel_audio_layer_configs.empty()) {
121 LOG(ERROR) << "No channel audio layer configs provided.";
122 return nullptr;
123 }
124 const auto& highest_channel_audio_layer_config =
125 scalable_channel_layout_config.channel_audio_layer_configs.back();
126 const auto& ordered_labels =
127 ChannelLabel::LookupEarChannelOrderFromScalableLoudspeakerLayout(
128 highest_channel_audio_layer_config.loudspeaker_layout,
129 highest_channel_audio_layer_config.expanded_loudspeaker_layout);
130 if (!ordered_labels.ok()) {
131 LOG(ERROR) << ordered_labels.status();
132 return nullptr;
133 }
134
135 const auto& input_key = LookupInputKeyFromLoudspeakerLayout(
136 highest_channel_audio_layer_config.loudspeaker_layout,
137 highest_channel_audio_layer_config.expanded_loudspeaker_layout);
138 if (!input_key.ok()) {
139 LOG(ERROR) << input_key.status();
140 return nullptr;
141 }
142 const auto& output_key =
143 renderer_utils::LookupOutputKeyFromPlaybackLayout(playback_layout);
144 if (!output_key.ok()) {
145 LOG(ERROR) << output_key.status();
146 return nullptr;
147 }
148
149 const auto& gains = LookupPrecomputedGains(*input_key, *output_key);
150 if (!gains.ok()) {
151 LOG(ERROR) << gains.status();
152 return nullptr;
153 }
154
155 int32_t num_output_channels = 0;
156 if (!MixPresentationObu::GetNumChannelsFromLayout(playback_layout,
157 num_output_channels)
158 .ok()) {
159 return nullptr;
160 }
161
162 return absl::WrapUnique(new AudioElementRendererChannelToChannel(
163 *input_key, *output_key, static_cast<size_t>(num_output_channels),
164 num_samples_per_frame, *ordered_labels, *gains));
165 }
166
RenderSamples(absl::Span<const std::vector<InternalSampleType>> samples_to_render,std::vector<InternalSampleType> & rendered_samples)167 absl::Status AudioElementRendererChannelToChannel::RenderSamples(
168 absl::Span<const std::vector<InternalSampleType>> samples_to_render,
169 std::vector<InternalSampleType>& rendered_samples) {
170 // Render the samples.
171 RETURN_IF_NOT_OK(RenderChannelLayoutToLoudspeakers(
172 samples_to_render, current_labeled_frame_->demixing_params,
173 ordered_labels_, input_key_, output_key_, gains_, rendered_samples));
174
175 return absl::OkStatus();
176 }
177
178 } // namespace iamf_tools
179