1 | /* |
2 | Copyright 2018 Google Inc. All Rights Reserved. |
3 | |
4 | Licensed under the Apache License, Version 2.0 (the "License"); |
5 | you may not use this file except in compliance with the License. |
6 | You may obtain a copy of the License at |
7 | |
8 | http://www.apache.org/licenses/LICENSE-2.0 |
9 | |
10 | Unless required by applicable law or agreed to in writing, software |
11 | distributed under the License is distributed on an "AS-IS" BASIS, |
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | See the License for the specific language governing permissions and |
14 | limitations 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 | |
31 | namespace vraudio { |
32 | |
33 | GraphManager::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 | |
75 | void 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 | |
110 | void 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 | |
148 | void 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 | |
154 | const 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 | |
166 | const AudioBuffer* GraphManager::GetStereoBuffer() const { |
167 | return stereo_mixer_node_->GetOutputBuffer(); |
168 | } |
169 | |
170 | const AudioBuffer *GraphManager::GetReverbBuffer() const |
171 | { |
172 | return reverb_node_->GetOutputBuffer(); |
173 | } |
174 | |
175 | size_t GraphManager::GetNumMaxAmbisonicChannels() const { |
176 | return GetNumPeriphonicComponents(ambisonic_order: config_.max_ambisonic_order); |
177 | } |
178 | |
179 | bool GraphManager::GetRoomEffectsEnabled() const { |
180 | return room_effects_enabled_; |
181 | } |
182 | |
183 | void GraphManager::UpdateRoomReflections() { reflections_node_->Update(); } |
184 | |
185 | void GraphManager::UpdateRoomReverb() { reverb_node_->Update(); } |
186 | |
187 | void 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 | |
195 | void 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 | |
205 | void 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 | |
222 | void 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 | |
238 | void 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 | |
249 | std::shared_ptr<SinkNode> GraphManager::GetSinkNode() { return output_node_; } |
250 | |
251 | void GraphManager::Process() { |
252 | |
253 | output_node_->ReadInputs(); |
254 | } |
255 | |
256 | AudioBuffer* 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 | |
264 | void 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 | |
280 | std::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 | |