1/*
2Copyright 2018 Google Inc. All Rights Reserved.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS-IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17#include "graph/graph_manager.h"
18
19#include <functional>
20
21#include "ambisonics/utils.h"
22#include "base/constants_and_types.h"
23#include "base/logging.h"
24#include "graph/foa_rotator_node.h"
25#include "graph/gain_node.h"
26#include "graph/hoa_rotator_node.h"
27#include "graph/mono_from_soundfield_node.h"
28#include "graph/near_field_effect_node.h"
29#include "graph/occlusion_node.h"
30
31namespace vraudio {
32
33GraphManager::GraphManager(const SystemSettings& system_settings)
34 :
35 room_effects_enabled_(true),
36 config_(GlobalConfig()),
37 system_settings_(system_settings),
38 fft_manager_(system_settings.GetFramesPerBuffer()),
39 output_node_(std::make_shared<SinkNode>()) {
40 CHECK_LE(system_settings.GetFramesPerBuffer(), kMaxSupportedNumFrames);
41
42 stereo_mixer_node_ =
43 std::make_shared<MixerNode>(args: system_settings_, args: kNumStereoChannels);
44 output_node_->Connect(publisher_node: stereo_mixer_node_);
45
46 /// Initialize the Ambisonic Lookup Table.
47 lookup_table_.reset(p: new AmbisonicLookupTable(config_.max_ambisonic_order));
48 // Initialize the Ambisonic Renderer subgraphs.
49 for (const auto& sh_hrir_filename_itr : config_.sh_hrir_filenames) {
50 const int ambisonic_order = sh_hrir_filename_itr.first;
51 const auto& sh_hrir_filename = sh_hrir_filename_itr.second;
52 InitializeAmbisonicRendererGraph(ambisonic_order, sh_hrir_filename);
53 // Initialize the Ambisonic Mixing Encoders for HRTF sound object rendering.
54 ambisonic_mixing_encoder_nodes_[ambisonic_order] =
55 std::make_shared<AmbisonicMixingEncoderNode>(
56 args: system_settings_, args&: *lookup_table_, args: ambisonic_order);
57 ambisonic_mixer_nodes_[ambisonic_order]->Connect(
58 publisher_node: ambisonic_mixing_encoder_nodes_[ambisonic_order]);
59 }
60
61 // Stereo mixing panner node used in non-HRTF sound object rendering.
62 stereo_mixing_panner_node_ =
63 std::make_shared<StereoMixingPannerNode>(args: system_settings_);
64 stereo_mixer_node_->Connect(publisher_node: stereo_mixing_panner_node_);
65
66 // Initialize room effects graphs.
67 InitializeReflectionsGraph();
68 InitializeReverbGraph();
69 // Initialize ambisonic output mixer.
70 ambisonic_output_mixer_.reset(
71 p: new Mixer(GetNumPeriphonicComponents(ambisonic_order: config_.max_ambisonic_order),
72 system_settings.GetFramesPerBuffer()));
73}
74
75void GraphManager::CreateAmbisonicSource(SourceId ambisonic_source_id,
76 size_t num_channels) {
77 DCHECK(source_nodes_.find(ambisonic_source_id) == source_nodes_.end());
78 // Create a new |ambisonic_source_node| and register to |source_nodes_|.
79 auto ambisonic_source_node = std::make_shared<BufferedSourceNode>(
80 args&: ambisonic_source_id, args&: num_channels, args: system_settings_.GetFramesPerBuffer());
81 source_nodes_[ambisonic_source_id] = ambisonic_source_node;
82
83 // Connect |ambisonic_source_node| to the ambisonic decoding pipeline.
84 const int ambisonic_order = GetPeriphonicAmbisonicOrder(num_components: num_channels);
85 auto direct_attenuation_node =
86 std::make_shared<GainNode>(args&: ambisonic_source_id, args&: num_channels,
87 args: AttenuationType::kDirect, args: system_settings_);
88 direct_attenuation_node->Connect(publisher_node: ambisonic_source_node);
89 if (ambisonic_order == 1) {
90 // First order case.
91 auto foa_rotator_node =
92 std::make_shared<FoaRotatorNode>(args&: ambisonic_source_id, args: system_settings_);
93 foa_rotator_node->Connect(publisher_node: direct_attenuation_node);
94 ambisonic_mixer_nodes_[ambisonic_order]->Connect(publisher_node: foa_rotator_node);
95 } else {
96 // Higher orders case.
97 auto hoa_rotator_node = std::make_shared<HoaRotatorNode>(
98 args&: ambisonic_source_id, args: system_settings_, args: ambisonic_order);
99 hoa_rotator_node->Connect(publisher_node: direct_attenuation_node);
100 ambisonic_mixer_nodes_[ambisonic_order]->Connect(publisher_node: hoa_rotator_node);
101 }
102 // Connect to room effects rendering pipeline.
103 auto mono_from_soundfield_node = std::make_shared<MonoFromSoundfieldNode>(
104 args&: ambisonic_source_id, args: system_settings_);
105 mono_from_soundfield_node->Connect(publisher_node: ambisonic_source_node);
106 reflections_gain_mixer_node_->Connect(publisher_node: mono_from_soundfield_node);
107 reverb_gain_mixer_node_->Connect(publisher_node: mono_from_soundfield_node);
108}
109
110void GraphManager::CreateSoundObjectSource(SourceId sound_object_source_id,
111 int ambisonic_order,
112 bool enable_hrtf,
113 bool enable_direct_rendering) {
114 DCHECK(source_nodes_.find(sound_object_source_id) == source_nodes_.end());
115 // Create a new |sound_object_source_node| and register to |source_nodes_|.
116 auto sound_object_source_node = std::make_shared<BufferedSourceNode>(
117 args&: sound_object_source_id, args: kNumMonoChannels,
118 args: system_settings_.GetFramesPerBuffer());
119 source_nodes_[sound_object_source_id] = sound_object_source_node;
120
121 // Create direct rendering pipeline.
122 if (enable_direct_rendering) {
123 auto direct_attenuation_node =
124 std::make_shared<GainNode>(args&: sound_object_source_id, args: kNumMonoChannels,
125 args: AttenuationType::kDirect, args: system_settings_);
126 direct_attenuation_node->Connect(publisher_node: sound_object_source_node);
127 auto occlusion_node = std::make_shared<OcclusionNode>(
128 args&: sound_object_source_id, args: system_settings_);
129 occlusion_node->Connect(publisher_node: direct_attenuation_node);
130 auto near_field_effect_node = std::make_shared<NearFieldEffectNode>(
131 args&: sound_object_source_id, args: system_settings_);
132
133 if (enable_hrtf) {
134 ambisonic_mixing_encoder_nodes_[ambisonic_order]->Connect(publisher_node: occlusion_node);
135 } else {
136 stereo_mixing_panner_node_->Connect(publisher_node: occlusion_node);
137 }
138
139 near_field_effect_node->Connect(publisher_node: occlusion_node);
140 stereo_mixer_node_->Connect(publisher_node: near_field_effect_node);
141 }
142
143 // Connect to room effects rendering pipeline.
144 reflections_gain_mixer_node_->Connect(publisher_node: sound_object_source_node);
145 reverb_gain_mixer_node_->Connect(publisher_node: sound_object_source_node);
146}
147
148void GraphManager::EnableRoomEffects(bool enable) {
149 room_effects_enabled_ = enable;
150 reflections_gain_mixer_node_->SetMute(!room_effects_enabled_);
151 reverb_gain_mixer_node_->SetMute(!room_effects_enabled_);
152}
153
154const AudioBuffer* GraphManager::GetAmbisonicBuffer() const {
155 ambisonic_output_mixer_->Reset();
156 for (const auto& ambisonic_mixer_node_itr : ambisonic_mixer_nodes_) {
157 const auto* ambisonic_buffer =
158 ambisonic_mixer_node_itr.second->GetOutputBuffer();
159 if (ambisonic_buffer != nullptr) {
160 ambisonic_output_mixer_->AddInput(input: *ambisonic_buffer);
161 }
162 }
163 return ambisonic_output_mixer_->GetOutput();
164}
165
166const AudioBuffer* GraphManager::GetStereoBuffer() const {
167 return stereo_mixer_node_->GetOutputBuffer();
168}
169
170const AudioBuffer *GraphManager::GetReverbBuffer() const
171{
172 return reverb_node_->GetOutputBuffer();
173}
174
175size_t GraphManager::GetNumMaxAmbisonicChannels() const {
176 return GetNumPeriphonicComponents(ambisonic_order: config_.max_ambisonic_order);
177}
178
179bool GraphManager::GetRoomEffectsEnabled() const {
180 return room_effects_enabled_;
181}
182
183void GraphManager::UpdateRoomReflections() { reflections_node_->Update(); }
184
185void GraphManager::UpdateRoomReverb() { reverb_node_->Update(); }
186
187void GraphManager::InitializeReverbGraph() {
188 reverb_gain_mixer_node_ = std::make_shared<GainMixerNode>(
189 args: AttenuationType::kReverb, args: system_settings_, args: kNumMonoChannels);
190 reverb_node_ = std::make_shared<ReverbNode>(args: system_settings_, args: &fft_manager_);
191 reverb_node_->Connect(publisher_node: reverb_gain_mixer_node_);
192 stereo_mixer_node_->Connect(publisher_node: reverb_node_);
193}
194
195void GraphManager::InitializeReflectionsGraph() {
196 reflections_gain_mixer_node_ = std::make_shared<GainMixerNode>(
197 args: AttenuationType::kReflections, args: system_settings_, args: kNumMonoChannels);
198 reflections_node_ = std::make_shared<ReflectionsNode>(args: system_settings_);
199 reflections_node_->Connect(publisher_node: reflections_gain_mixer_node_);
200 // Reflections are limited to First Order Ambisonics to reduce complexity.
201 const int kAmbisonicOrder1 = 1;
202 ambisonic_mixer_nodes_[kAmbisonicOrder1]->Connect(publisher_node: reflections_node_);
203}
204
205void GraphManager::CreateAmbisonicPannerSource(SourceId sound_object_source_id,
206 bool enable_hrtf) {
207 DCHECK(source_nodes_.find(sound_object_source_id) == source_nodes_.end());
208 // Create a new |sound_object_source_node| and register to |source_nodes_|.
209 auto sound_object_source_node = std::make_shared<BufferedSourceNode>(
210 args&: sound_object_source_id, args: kNumMonoChannels,
211 args: system_settings_.GetFramesPerBuffer());
212 source_nodes_[sound_object_source_id] = sound_object_source_node;
213
214 if (enable_hrtf) {
215 ambisonic_mixing_encoder_nodes_[config_.max_ambisonic_order]->Connect(
216 publisher_node: sound_object_source_node);
217 } else {
218 stereo_mixing_panner_node_->Connect(publisher_node: sound_object_source_node);
219 }
220}
221
222void GraphManager::CreateStereoSource(SourceId stereo_source_id) {
223 DCHECK(source_nodes_.find(stereo_source_id) == source_nodes_.end());
224 // Create a new |stereo_source_node| and register to |source_nodes_|.
225 auto stereo_source_node = std::make_shared<BufferedSourceNode>(
226 args&: stereo_source_id, args: kNumStereoChannels,
227 args: system_settings_.GetFramesPerBuffer());
228 source_nodes_[stereo_source_id] = stereo_source_node;
229
230 // Connect |stereo_source_node| to the stereo rendering pipeline.
231 auto gain_node =
232 std::make_shared<GainNode>(args&: stereo_source_id, args: kNumStereoChannels,
233 args: AttenuationType::kInput, args: system_settings_);
234 gain_node->Connect(publisher_node: stereo_source_node);
235 stereo_mixer_node_->Connect(publisher_node: gain_node);
236}
237
238void GraphManager::DestroySource(SourceId source_id) {
239 auto source_node = LookupSourceNode(source_id);
240 if (source_node != nullptr) {
241 // Disconnect the source from the graph.
242 source_node->MarkEndOfStream();
243 output_node_->CleanUp();
244 // Unregister the source from |source_nodes_|.
245 source_nodes_.erase(x: source_id);
246 }
247}
248
249std::shared_ptr<SinkNode> GraphManager::GetSinkNode() { return output_node_; }
250
251void GraphManager::Process() {
252
253 output_node_->ReadInputs();
254}
255
256AudioBuffer* GraphManager::GetMutableAudioBuffer(SourceId source_id) {
257 auto source_node = LookupSourceNode(source_id);
258 if (source_node == nullptr) {
259 return nullptr;
260 }
261 return source_node->GetMutableAudioBufferAndSetNewBufferFlag();
262}
263
264void GraphManager::InitializeAmbisonicRendererGraph(
265 int ambisonic_order, const std::string& sh_hrir_filename) {
266 CHECK_LE(ambisonic_order, config_.max_ambisonic_order);
267 const size_t num_channels = GetNumPeriphonicComponents(ambisonic_order);
268 // Create binaural decoder pipeline.
269 ambisonic_mixer_nodes_[ambisonic_order] =
270 std::make_shared<MixerNode>(args: system_settings_, args: num_channels);
271 auto ambisonic_binaural_decoder_node =
272 std::make_shared<AmbisonicBinauralDecoderNode>(
273 args: system_settings_, args&: ambisonic_order, args: sh_hrir_filename, args: &fft_manager_,
274 args: &resampler_);
275 ambisonic_binaural_decoder_node->Connect(
276 publisher_node: ambisonic_mixer_nodes_[ambisonic_order]);
277 stereo_mixer_node_->Connect(publisher_node: ambisonic_binaural_decoder_node);
278}
279
280std::shared_ptr<BufferedSourceNode> GraphManager::LookupSourceNode(
281 SourceId source_id) {
282 auto source_node_itr = source_nodes_.find(x: source_id);
283 if (source_node_itr == source_nodes_.end()) {
284 LOG(WARNING) << "Source node " << source_id << " not found";
285 return nullptr;
286 }
287 return source_node_itr->second;
288}
289
290} // namespace vraudio
291

source code of qtmultimedia/src/3rdparty/resonance-audio/resonance_audio/graph/graph_manager.cc